Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions .bumpversion.cfg
Original file line numberDiff line numberDiff line change
@@ -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}**

10 changes: 9 additions & 1 deletion .travis.yml
Original file line numberDiff line numberDiff line change
Expand Up@@ -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
12 changes: 12 additions & 0 deletions MANIFEST.in
Original file line numberDiff line numberDiff line change
@@ -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
7 changes: 6 additions & 1 deletion appveyor.yml
Original file line numberDiff line numberDiff line change
Expand Up@@ -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'
Binary file addedci/SharpCover/Counter.dll
Binary file not shown.
Binary file addedci/SharpCover/Mono.Cecil.Mdb.dll
Binary file not shown.
Binary file addedci/SharpCover/Mono.Cecil.Pdb.dll
Binary file not shown.
Binary file addedci/SharpCover/Mono.Cecil.Rocks.dll
Binary file not shown.
Binary file addedci/SharpCover/Mono.Cecil.dll
Binary file not shown.
Binary file addedci/SharpCover/Newtonsoft.Json.dll
Binary file not shown.
Binary file addedci/SharpCover/SharpCover.exe
Binary file not shown.
111 changes: 111 additions & 0 deletions ci/appveyor-download.py
Original file line numberDiff line numberDiff line change
@@ -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)
7 changes: 4 additions & 3 deletions ci/appveyor_build_recipe.ps1
Original file line numberDiff line numberDiff line change
@@ -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
Expand Down
1 change: 1 addition & 0 deletions cover.bat
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
.\packages\OpenCover.4.6.519\tools\OpenCover.Console.exe -target:test.bat -register:user -searchdirs:.\src\runtime\bin\x64\DebugWin\
1 change: 1 addition & 0 deletions report.bat
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
.\packages\ReportGenerator.2.5.2\tools\reportgenerator.exe -reports:results.xml -targetdir:htmlcov
2 changes: 2 additions & 0 deletions setup.cfg
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,4 @@
# Don't combine `.bumpversion.cfg` with `setup.cfg`. Messes up formatting.

[tool:pytest]
xfail_strict = True
21 changes: 1 addition & 20 deletions setup.py
Original file line numberDiff line numberDiff line change
Expand Up@@ -7,7 +7,6 @@
"""

import collections
import fnmatch
import glob
import os
import platform
Expand DownExpand Up@@ -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:
Expand DownExpand Up@@ -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}", [
Expand Down
5 changes: 5 additions & 0 deletions sharpcover.json
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
{
"assemblies": ["Tests/Domain.Tests/bin/Release/Codecov.Example.Domain.dll"],
"typeInclude": "Codecov.Example.*",
"typeExclude": "Codecov.Example.*Tests*"
}
1 change: 0 additions & 1 deletion src/embed_tests/Python.EmbeddingTest.csproj
Original file line numberDiff line numberDiff line change
Expand Up@@ -81,7 +81,6 @@
<Compile Include="pylong.cs" />
<Compile Include="pyobject.cs" />
<Compile Include="pythonexception.cs" />
<Compile Include="pytuple.cs" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\runtime\Python.Runtime.csproj">
Expand Down
2 changes: 2 additions & 0 deletions src/embed_tests/packages.config
Original file line numberDiff line numberDiff line change
Expand Up@@ -2,4 +2,6 @@
<packages>
<package id="NUnit" version="3.6.0" targetFramework="net40" />
<package id="NUnit.ConsoleRunner" version="3.6.0" targetFramework="net40" />
<package id="OpenCover" version="4.6.519" targetFramework="net40" />
<package id="ReportGenerator" version="2.5.2" targetFramework="net40" />
</packages>
1 change: 1 addition & 0 deletions src/runtime/pythonengine.cs
Original file line numberDiff line numberDiff line change
Expand Up@@ -157,6 +157,7 @@ public static void Initialize(IEnumerable<string> args)
"import atexit, clr\n" +
"atexit.register(clr._AtExit)\n"
PyObject r = PythonEngine.RunString(code);
Exceptions.Clear();
if (r != null)
{
r.Dispose();
Expand Down
42 changes: 17 additions & 25 deletions src/runtime/runtime.cs
Original file line numberDiff line numberDiff line change
Expand Up@@ -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)]
Expand DownExpand Up@@ -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;
}
Expand DownExpand Up@@ -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,
Expand Down
5 changes: 5 additions & 0 deletions test.bat
Original file line numberDiff line numberDiff line change
@@ -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