diff --git a/.bumpversion.cfg b/.bumpversion.cfg
new file mode 100644
index 000000000..bca342cbb
--- /dev/null
+++ b/.bumpversion.cfg
@@ -0,0 +1,12 @@
+[bumpversion]
+current_version = 2.2.2
+
+[bumpversion:file:src/runtime/resources/clr.py]
+
+[bumpversion:file:setup.py]
+
+[bumpversion:file:CHANGELOG.md]
+search = **unreleased**
+replace = **unreleased**
+ **v{new_version}**
+
diff --git a/.travis.yml b/.travis.yml
index 5b56cf4a6..50580a711 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -53,7 +53,15 @@ after_success:
# - python tools/geninterop/geninterop.py
# Waiting on mono-cov support or SharpCover
- - codecov
+ - ls
notifications:
email: false
+
+before_cache:
+ - rm -rf $HOME/.cache/pip/log
+
+cache:
+ directories:
+ - $HOME/.cache/pip
+ - packages
diff --git a/MANIFEST.in b/MANIFEST.in
new file mode 100644
index 000000000..291dea6b4
--- /dev/null
+++ b/MANIFEST.in
@@ -0,0 +1,12 @@
+graft src
+graft tools
+
+include AUTHORS.md
+include CHANGELOG.md
+include LICENSE
+include README.md
+
+include Python.Runtime.dll.config
+include pythonnet.sln
+
+global-exclude *.py[cod] __pycache__ *.so *.dylib
diff --git a/appveyor.yml b/appveyor.yml
index 080aa3807..f873fb997 100644
--- a/appveyor.yml
+++ b/appveyor.yml
@@ -64,7 +64,12 @@ test_script:
on_finish:
# Upload coverage
- - codecov
+ - dir
artifacts:
- path: dist\*
+
+cache:
+ # preserve `packages` directory; will reset if `packages.config` is modified
+ - packages -> **\packages.config
+ - '%LOCALAPPDATA%\pip\Cache -> appveyor.yml'
diff --git a/ci/SharpCover/Counter.dll b/ci/SharpCover/Counter.dll
new file mode 100644
index 000000000..364b7dcf3
Binary files /dev/null and b/ci/SharpCover/Counter.dll differ
diff --git a/ci/SharpCover/Mono.Cecil.Mdb.dll b/ci/SharpCover/Mono.Cecil.Mdb.dll
new file mode 100644
index 000000000..b1156282c
Binary files /dev/null and b/ci/SharpCover/Mono.Cecil.Mdb.dll differ
diff --git a/ci/SharpCover/Mono.Cecil.Pdb.dll b/ci/SharpCover/Mono.Cecil.Pdb.dll
new file mode 100644
index 000000000..149ff2a31
Binary files /dev/null and b/ci/SharpCover/Mono.Cecil.Pdb.dll differ
diff --git a/ci/SharpCover/Mono.Cecil.Rocks.dll b/ci/SharpCover/Mono.Cecil.Rocks.dll
new file mode 100644
index 000000000..b13c40942
Binary files /dev/null and b/ci/SharpCover/Mono.Cecil.Rocks.dll differ
diff --git a/ci/SharpCover/Mono.Cecil.dll b/ci/SharpCover/Mono.Cecil.dll
new file mode 100644
index 000000000..454f1fb69
Binary files /dev/null and b/ci/SharpCover/Mono.Cecil.dll differ
diff --git a/ci/SharpCover/Newtonsoft.Json.dll b/ci/SharpCover/Newtonsoft.Json.dll
new file mode 100644
index 000000000..81639f9b1
Binary files /dev/null and b/ci/SharpCover/Newtonsoft.Json.dll differ
diff --git a/ci/SharpCover/SharpCover.exe b/ci/SharpCover/SharpCover.exe
new file mode 100644
index 000000000..4975465b0
Binary files /dev/null and b/ci/SharpCover/SharpCover.exe differ
diff --git a/ci/appveyor-download.py b/ci/appveyor-download.py
new file mode 100644
index 000000000..d4bf772fc
--- /dev/null
+++ b/ci/appveyor-download.py
@@ -0,0 +1,111 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+
+"""Use the AppVeyor API to download Windows artifacts.
+
+From: https://bitbucket.org/ned/coveragepy/src/tip/ci/download_appveyor.py
+# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0
+# For details: https://bitbucket.org/ned/coveragepy/src/default/NOTICE.txt
+"""
+
+from __future__ import unicode_literals
+
+import argparse
+import os
+import zipfile
+
+import requests
+
+
+def make_auth_headers():
+ """Make the authentication headers needed to use the Appveyor API."""
+ path = os.path.expanduser("~/.appveyor.token")
+ if not os.path.exists(path):
+ raise RuntimeError(
+ "Please create a file named `.appveyor.token` in your home directory. "
+ "You can get the token from https://ci.appveyor.com/api-token"
+ )
+ with open(path) as f:
+ token = f.read().strip()
+
+ headers = {
+ 'Authorization': 'Bearer {}'.format(token),
+ }
+ return headers
+
+
+def download_latest_artifacts(account_project, build_id):
+ """Download all the artifacts from the latest build."""
+ if build_id is None:
+ url = "https://ci.appveyor.com/api/projects/{}".format(account_project)
+ else:
+ url = "https://ci.appveyor.com/api/projects/{}/build/{}".format(account_project, build_id)
+ build = requests.get(url, headers=make_auth_headers()).json()
+ jobs = build['build']['jobs']
+ print(u"Build {0[build][version]}, {1} jobs: {0[build][message]}".format(build, len(jobs)))
+
+ for job in jobs:
+ name = job['name']
+ print(u" {0}: {1[status]}, {1[artifactsCount]} artifacts".format(name, job))
+
+ url = "https://ci.appveyor.com/api/buildjobs/{}/artifacts".format(job['jobId'])
+ response = requests.get(url, headers=make_auth_headers())
+ artifacts = response.json()
+
+ for artifact in artifacts:
+ is_zip = artifact['type'] == "Zip"
+ filename = artifact['fileName']
+ print(u" {0}, {1} bytes".format(filename, artifact['size']))
+
+ url = "https://ci.appveyor.com/api/buildjobs/{}/artifacts/{}".format(job['jobId'], filename)
+ download_url(url, filename, make_auth_headers())
+
+ if is_zip:
+ unpack_zipfile(filename)
+ os.remove(filename)
+
+
+def ensure_dirs(filename):
+ """Make sure the directories exist for `filename`."""
+ dirname = os.path.dirname(filename)
+ if dirname and not os.path.exists(dirname):
+ os.makedirs(dirname)
+
+
+def download_url(url, filename, headers):
+ """Download a file from `url` to `filename`."""
+ ensure_dirs(filename)
+ response = requests.get(url, headers=headers, stream=True)
+ if response.status_code == 200:
+ with open(filename, 'wb') as f:
+ for chunk in response.iter_content(16 * 1024):
+ f.write(chunk)
+ else:
+ print(u" Error downloading {}: {}".format(url, response))
+
+
+def unpack_zipfile(filename):
+ """Unpack a zipfile, using the names in the zip."""
+ with open(filename, 'rb') as fzip:
+ z = zipfile.ZipFile(fzip)
+ for name in z.namelist():
+ print(u" extracting {}".format(name))
+ ensure_dirs(name)
+ z.extract(name)
+
+
+parser = argparse.ArgumentParser(description='Download artifacts from AppVeyor.')
+parser.add_argument('--id',
+ metavar='PROJECT_ID',
+ default='pythonnet/pythonnet',
+ help='Project ID in AppVeyor.')
+parser.add_argument('build',
+ nargs='?',
+ metavar='BUILD_ID',
+ help='Build ID in AppVeyor. Eg: master-123')
+
+if __name__ == "__main__":
+ # import logging
+ # logging.basicConfig(level="DEBUG")
+ args = parser.parse_args()
+ download_latest_artifacts(args.id, args.build)
diff --git a/ci/appveyor_build_recipe.ps1 b/ci/appveyor_build_recipe.ps1
index cd11f57b9..01584a4b3 100644
--- a/ci/appveyor_build_recipe.ps1
+++ b/ci/appveyor_build_recipe.ps1
@@ -1,11 +1,12 @@
-if ($env:APPVEYOR_PULL_REQUEST_NUMBER) {
+if ($env:APPVEYOR_PULL_REQUEST_NUMBER -ne 2) {
# Update PATH, and keep a copy to restore at end of this PowerShell script
$old_path = $env:path
$env:path = "$env:CONDA_BLD;$env:CONDA_BLD\Scripts;" + $env:path
Write-Host "Starting Conda Update/Install" -ForegroundColor "Green"
- conda update conda -q -y
- conda install conda-build jinja2 anaconda-client -q -y
+ conda config --set always_yes true
+ # conda config --set auto_update_conda False
+ conda install conda-build jinja2 anaconda-client -q
Write-Host "Starting Conda Recipe build" -ForegroundColor "Green"
conda build conda.recipe -q --dirty
diff --git a/cover.bat b/cover.bat
new file mode 100644
index 000000000..9e0239a6d
--- /dev/null
+++ b/cover.bat
@@ -0,0 +1 @@
+.\packages\OpenCover.4.6.519\tools\OpenCover.Console.exe -target:test.bat -register:user -searchdirs:.\src\runtime\bin\x64\DebugWin\
diff --git a/report.bat b/report.bat
new file mode 100644
index 000000000..0aa72e03d
--- /dev/null
+++ b/report.bat
@@ -0,0 +1 @@
+.\packages\ReportGenerator.2.5.2\tools\reportgenerator.exe -reports:results.xml -targetdir:htmlcov
diff --git a/setup.cfg b/setup.cfg
index 5ee7224f7..dace18747 100644
--- a/setup.cfg
+++ b/setup.cfg
@@ -1,2 +1,4 @@
+# Don't combine `.bumpversion.cfg` with `setup.cfg`. Messes up formatting.
+
[tool:pytest]
xfail_strict = True
diff --git a/setup.py b/setup.py
index f2790ddd3..17e156832 100644
--- a/setup.py
+++ b/setup.py
@@ -7,7 +7,6 @@
"""
import collections
-import fnmatch
import glob
import os
import platform
@@ -102,24 +101,6 @@ def _get_interop_filename():
return os.path.join("src", "runtime", interop_filename)
-def _get_source_files():
- """Walk project and collect the files needed for ext_module"""
- for ext in (".sln", ".config"):
- for path in glob.glob("*" + ext):
- yield path
-
- for root, dirnames, filenames in os.walk("src"):
- for ext in (".cs", ".csproj", ".snk", ".config",
- ".py", ".c", ".h", ".ico"):
- for filename in fnmatch.filter(filenames, "*" + ext):
- yield os.path.join(root, filename)
-
- for root, dirnames, filenames in os.walk("tools"):
- for ext in (".exe", ".py", ".c", ".h"):
- for filename in fnmatch.filter(filenames, "*" + ext):
- yield os.path.join(root, filename)
-
-
def _get_long_description():
"""Helper to populate long_description for pypi releases"""
try:
@@ -377,7 +358,7 @@ def run(self):
setup_requires=setup_requires,
long_description=_get_long_description(),
ext_modules=[
- Extension("clr", sources=list(_get_source_files()))
+ Extension("clr", sources=['', ])
],
data_files=[
("{install_platlib}", [
diff --git a/sharpcover.json b/sharpcover.json
new file mode 100644
index 000000000..ed4ef9d9a
--- /dev/null
+++ b/sharpcover.json
@@ -0,0 +1,5 @@
+{
+ "assemblies": ["Tests/Domain.Tests/bin/Release/Codecov.Example.Domain.dll"],
+ "typeInclude": "Codecov.Example.*",
+ "typeExclude": "Codecov.Example.*Tests*"
+}
diff --git a/src/embed_tests/Python.EmbeddingTest.csproj b/src/embed_tests/Python.EmbeddingTest.csproj
index a2e92ed19..87ca2d3ad 100644
--- a/src/embed_tests/Python.EmbeddingTest.csproj
+++ b/src/embed_tests/Python.EmbeddingTest.csproj
@@ -81,7 +81,6 @@
-
diff --git a/src/embed_tests/packages.config b/src/embed_tests/packages.config
index 4cb01d3be..0ed7bdb37 100644
--- a/src/embed_tests/packages.config
+++ b/src/embed_tests/packages.config
@@ -2,4 +2,6 @@
+
+
diff --git a/src/runtime/pythonengine.cs b/src/runtime/pythonengine.cs
index e9fa888a9..03f2d8520 100644
--- a/src/runtime/pythonengine.cs
+++ b/src/runtime/pythonengine.cs
@@ -157,6 +157,7 @@ public static void Initialize(IEnumerable args)
"import atexit, clr\n" +
"atexit.register(clr._AtExit)\n";
PyObject r = PythonEngine.RunString(code);
+ Exceptions.Clear();
if (r != null)
{
r.Dispose();
diff --git a/src/runtime/runtime.cs b/src/runtime/runtime.cs
index 22b590657..131a44a6f 100644
--- a/src/runtime/runtime.cs
+++ b/src/runtime/runtime.cs
@@ -1490,6 +1490,19 @@ internal static IntPtr PyString_FromString(string value)
return PyString_FromStringAndSize(value, value.Length);
}
+ internal unsafe static IntPtr StringToHeap(string s)
+ {
+ var bufLength = s.Length * 4;
+ IntPtr mem = Marshal.AllocHGlobal(bufLength);
+
+ fixed (char* ps = s)
+ {
+ Encoding.UTF32.GetBytes(ps, s.Length, (byte*)mem, bufLength);
+ }
+
+ return mem;
+ }
+
#if PYTHON3
[DllImport(Runtime.dll, CallingConvention = CallingConvention.Cdecl,
ExactSpelling = true, CharSet = CharSet.Ansi)]
@@ -1659,16 +1672,9 @@ internal static unsafe IntPtr PyUnicode_FromKindAndString(int kind,
string s,
int size)
{
- var bufLength = Math.Max(s.Length, size) * 4;
-
- IntPtr mem = Marshal.AllocHGlobal(bufLength);
+ IntPtr mem = StringToHeap(s);
try
{
- fixed (char* ps = s)
- {
- Encoding.UTF32.GetBytes(ps, s.Length, (byte*)mem, bufLength);
- }
-
var result = PyUnicode_FromKindAndString(kind, mem, size);
return result;
}
@@ -1724,23 +1730,9 @@ internal unsafe static extern IntPtr
internal static unsafe IntPtr PyUnicode_FromUnicode(string s, int size)
{
- var bufLength = Math.Max(s.Length, size) * 4;
-
- IntPtr mem = Marshal.AllocHGlobal(bufLength);
- try
- {
- fixed (char* ps = s)
- {
- Encoding.UTF32.GetBytes(ps, s.Length, (byte*)mem, bufLength);
- }
-
- var result = PyUnicode_FromUnicode(mem, size);
- return result;
- }
- finally
- {
- Marshal.FreeHGlobal(mem);
- }
+ IntPtr mem = StringToHeap(s);
+ var result = PyUnicode_FromUnicode(mem, size);
+ return result;
}
[DllImport(Runtime.dll, CallingConvention = CallingConvention.Cdecl,
diff --git a/test.bat b/test.bat
new file mode 100644
index 000000000..c8c9c595d
--- /dev/null
+++ b/test.bat
@@ -0,0 +1,5 @@
+:: Run embed_tests. Disabled, due to regressions needing fixing
+:: .\packages\NUnit.Runners.2.6.2\tools\nunit-console.exe src\embed_tests\bin\x64\DebugWin\Python.EmbeddingTest.dll /noshadow
+
+:: Run Python tests
+python .\src\tests\runtests.py