From 435b6b3b7b6d32148fc5c4ce430e715bfdae6ac9 Mon Sep 17 00:00:00 2001 From: Benedikt Reinartz Date: Sat, 17 Sep 2022 09:06:03 +0200 Subject: [PATCH 01/10] Fix links to reference in documentation --- doc/usage.rst | 2 ++ 1 file changed, 2 insertions(+) diff --git a/doc/usage.rst b/doc/usage.rst index 2c10f62..23b61e5 100644 --- a/doc/usage.rst +++ b/doc/usage.rst @@ -1,6 +1,8 @@ Usage ===== +.. py:currentmodule:: clr_loader + Getting a runtime ----------------- From c1e337592f5bb7c1fced8e27dc65c408a0a8eb78 Mon Sep 17 00:00:00 2001 From: Benedikt Reinartz Date: Sat, 17 Sep 2022 17:01:02 +0200 Subject: [PATCH 02/10] Switch to Furo theme and use custom step (#33) The "published" step uses an ages old version of Sphinx (2.2.4). This should also fix issues with links to referenced functions. --- .github/workflows/docs.yml | 8 +++++--- doc/conf.py | 2 +- doc/requirements.txt | 6 +++++- 3 files changed, 11 insertions(+), 5 deletions(-) diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index ed89bc2..bce19d4 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -7,9 +7,11 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v1 - - uses: ammaraskar/sphinx-action@master - with: - docs-folder: doc/ + - name: Build Sphinx documentation + run: | + pip install -r doc/requirements.txt + sphinx-build doc/ ./doc/_build/html/ + - name: Upload artifact # Automatically uploads an artifact from the './_site' directory by default uses: actions/upload-pages-artifact@v1 diff --git a/doc/conf.py b/doc/conf.py index 5f97ad9..dac323a 100644 --- a/doc/conf.py +++ b/doc/conf.py @@ -45,7 +45,7 @@ # The theme to use for HTML and HTML Help pages. See the documentation for # a list of builtin themes. # -html_theme = "sphinx_rtd_theme" +html_theme = "furo" # Add any paths that contain custom static files (such as style sheets) here, # relative to this directory. They are copied after the builtin static files, diff --git a/doc/requirements.txt b/doc/requirements.txt index 483a4e9..97147a4 100644 --- a/doc/requirements.txt +++ b/doc/requirements.txt @@ -1 +1,5 @@ -sphinx_rtd_theme +sphinx + +# Theme, force pygments update +furo>=2022.9.15 +pygments>=2.7 From 1df0f23d7b5fe557c6082b8006247466db1a7ebb Mon Sep 17 00:00:00 2001 From: Benedikt Reinartz Date: Fri, 23 Sep 2022 07:34:31 +0200 Subject: [PATCH 03/10] Fix autodoc not loading clr_loader from the repository --- doc/conf.py | 42 +++++------------------------------------- 1 file changed, 5 insertions(+), 37 deletions(-) diff --git a/doc/conf.py b/doc/conf.py index dac323a..3e0f45d 100644 --- a/doc/conf.py +++ b/doc/conf.py @@ -1,34 +1,13 @@ -# Configuration file for the Sphinx documentation builder. -# -# This file only contains a selection of the most common options. For a full -# list see the documentation: -# https://www.sphinx-doc.org/en/master/usage/configuration.html - -# -- Path setup -------------------------------------------------------------- - -# If extensions (or modules to document with autodoc) are in another directory, -# add these directories to sys.path here. If the directory is relative to the -# documentation root, use os.path.abspath to make it absolute, like shown here. -# -# import os -# import sys -# sys.path.insert(0, os.path.abspath('.')) - - -# -- Project information ----------------------------------------------------- - project = "clr-loader" copyright = "2022, Benedikt Reinartz" author = "Benedikt Reinartz" - -# -- General configuration --------------------------------------------------- - -# Add any Sphinx extension module names here, as strings. They can be -# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom -# ones. extensions = ["sphinx.ext.autodoc"] +# Add parent to path for autodoc +import sys, os +sys.path.append(os.path.abspath('..')) + # autodoc_typehints = "both" # Add any paths that contain templates here, relative to this directory. @@ -38,16 +17,5 @@ # directories to ignore when looking for source files. # This pattern also affects html_static_path and html_extra_path. exclude_patterns = ["_build", "Thumbs.db", ".DS_Store"] - - -# -- Options for HTML output ------------------------------------------------- - -# The theme to use for HTML and HTML Help pages. See the documentation for -# a list of builtin themes. -# html_theme = "furo" - -# Add any paths that contain custom static files (such as style sheets) here, -# relative to this directory. They are copied after the builtin static files, -# so a file named "default.css" will overwrite the builtin "default.css". -html_static_path = ["_static"] +html_static_path = [] From cba6ebfdcd33eda54a1ae9e9bc81be92a74904bf Mon Sep 17 00:00:00 2001 From: DongGeon Lee Date: Mon, 10 Oct 2022 19:06:10 +0900 Subject: [PATCH 04/10] Fix to find dotnet root on 64-bits darwin system (#37) --- clr_loader/util/find.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/clr_loader/util/find.py b/clr_loader/util/find.py index daad769..c163240 100644 --- a/clr_loader/util/find.py +++ b/clr_loader/util/find.py @@ -42,7 +42,10 @@ def find_dotnet_root() -> Path: prog_files = Path(prog_files) dotnet_root = prog_files / "dotnet" elif sys.platform == "darwin": - dotnet_root = Path("/usr/local/share/dotnet") + if sys.maxsize > 2**32: # is_64bits + dotnet_root = Path("/usr/local/share/dotnet/x64") + else: + dotnet_root = Path("/usr/local/share/dotnet") if dotnet_root is not None and dotnet_root.is_dir(): return dotnet_root From 9c24c89ceabc608ed100bf9f884f98d2f7fe36f1 Mon Sep 17 00:00:00 2001 From: Benedikt Reinartz Date: Wed, 12 Oct 2022 20:07:43 +0200 Subject: [PATCH 05/10] Define self._handle up-front to prevent warning in __del__ Since initialization failures happen (usually) in `get_handle`, the instance would not have a proper `self._handle` defined and fail on `__del__` which checks for this field's value. --- clr_loader/hostfxr.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/clr_loader/hostfxr.py b/clr_loader/hostfxr.py index dce5c82..522fec5 100644 --- a/clr_loader/hostfxr.py +++ b/clr_loader/hostfxr.py @@ -13,6 +13,8 @@ class DotnetCoreRuntime(Runtime): def __init__(self, runtime_config: Path, dotnet_root: Path, **params: str): + self._handle = None + if _IS_SHUTDOWN: raise RuntimeError("Runtime can not be reinitialized") From 15ede715c5248164d1a6a6e4f5c921b83ab329b9 Mon Sep 17 00:00:00 2001 From: Benedikt Reinartz Date: Wed, 12 Oct 2022 20:08:30 +0200 Subject: [PATCH 06/10] Allow loading libhostfxr directly from the dotnet_root This is a prerequisite to load self-contained components (which is still not supported upstream, though). --- clr_loader/ffi/__init__.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/clr_loader/ffi/__init__.py b/clr_loader/ffi/__init__.py index 208824f..035541e 100644 --- a/clr_loader/ffi/__init__.py +++ b/clr_loader/ffi/__init__.py @@ -16,6 +16,7 @@ def load_hostfxr(dotnet_root: Path): hostfxr_name = _get_dll_name("hostfxr") + dotnet_root = dotnet_root.absolute() # This will fail as soon as .NET hits version 10, but hopefully by then # we'll have a more robust way of finding the libhostfxr @@ -28,6 +29,11 @@ def load_hostfxr(dotnet_root: Path): except Exception: pass + try: + return ffi.dlopen(str(dotnet_root / hostfxr_name)) + except Exception: + pass + raise RuntimeError(f"Could not find a suitable hostfxr library in {dotnet_root}") From ba0a3535e7518fc36d530e52aae38b46b5bbf82f Mon Sep 17 00:00:00 2001 From: Benedikt Reinartz Date: Fri, 14 Oct 2022 15:48:00 +0200 Subject: [PATCH 07/10] Format --- clr_loader/util/find.py | 2 +- doc/conf.py | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/clr_loader/util/find.py b/clr_loader/util/find.py index c163240..22ba843 100644 --- a/clr_loader/util/find.py +++ b/clr_loader/util/find.py @@ -42,7 +42,7 @@ def find_dotnet_root() -> Path: prog_files = Path(prog_files) dotnet_root = prog_files / "dotnet" elif sys.platform == "darwin": - if sys.maxsize > 2**32: # is_64bits + if sys.maxsize > 2**32: # is_64bits dotnet_root = Path("/usr/local/share/dotnet/x64") else: dotnet_root = Path("/usr/local/share/dotnet") diff --git a/doc/conf.py b/doc/conf.py index 3e0f45d..1ad2427 100644 --- a/doc/conf.py +++ b/doc/conf.py @@ -6,7 +6,8 @@ # Add parent to path for autodoc import sys, os -sys.path.append(os.path.abspath('..')) + +sys.path.append(os.path.abspath("..")) # autodoc_typehints = "both" From ba68d160173f508b89fc973fc1d5cc53e8dc052d Mon Sep 17 00:00:00 2001 From: Benedikt Reinartz Date: Fri, 14 Oct 2022 15:49:19 +0200 Subject: [PATCH 08/10] Parse the FXR version to properly select the newest one --- clr_loader/ffi/__init__.py | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/clr_loader/ffi/__init__.py b/clr_loader/ffi/__init__.py index 035541e..23debae 100644 --- a/clr_loader/ffi/__init__.py +++ b/clr_loader/ffi/__init__.py @@ -1,6 +1,6 @@ import sys from pathlib import Path -from typing import Optional +from typing import Optional, Tuple import cffi # type: ignore @@ -23,7 +23,7 @@ def load_hostfxr(dotnet_root: Path): hostfxr_path = dotnet_root / "host" / "fxr" hostfxr_paths = hostfxr_path.glob(f"?.*/{hostfxr_name}") - for hostfxr_path in reversed(sorted(hostfxr_paths)): + for hostfxr_path in reversed(sorted(hostfxr_paths, key=_path_to_version)): try: return ffi.dlopen(str(hostfxr_path)) except Exception: @@ -61,6 +61,15 @@ def load_netfx(): return ffi.dlopen(str(path)) +def _path_to_version(path: Path) -> Tuple[int, int, int]: + name = path.parent.name + try: + res = list(map(int, name.split("."))) + return tuple(res + [0, 0, 0])[:3] + except Exception: + return (0, 0, 0) + + def _get_dll_name(name: str) -> str: if sys.platform == "win32": return f"{name}.dll" From 0e7b2f16a76773aaad20ddee56cdab13a6e8cb0a Mon Sep 17 00:00:00 2001 From: Benedikt Reinartz Date: Fri, 14 Oct 2022 22:52:29 +0200 Subject: [PATCH 09/10] Split package build from testing (#39) --- .github/workflows/ci.yml | 57 ++++++++++++++++++++++++++++++++-------- pyproject.toml | 8 ++++-- 2 files changed, 52 insertions(+), 13 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 55f5d65..b4e25f9 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -7,14 +7,42 @@ on: [push, pull_request] jobs: build: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + with: + fetch-depth: 0 + - uses: actions/setup-dotnet@v1 + - uses: actions/setup-python@v4 + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install build + - name: Build + run: python -m build + - name: Upload source distribution + uses: actions/upload-artifact@v3 + with: + name: sdist + path: "dist/*.tar.gz" + if-no-files-found: error + - name: Upload wheel + uses: actions/upload-artifact@v3 + with: + name: wheel + path: "dist/*.whl" + if-no-files-found: error + + test: runs-on: ${{ matrix.os }} + needs: build strategy: matrix: os: [ubuntu-latest, windows-latest, macos-latest] python: ['3.10', '3.9', '3.8', '3.7'] # pypy3 steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - name: Setup .NET uses: actions/setup-dotnet@v1 @@ -22,19 +50,10 @@ jobs: dotnet-version: '6.0.x' - name: Set up Python ${{ matrix.python }} - uses: actions/setup-python@v2 + uses: actions/setup-python@v4 with: python-version: ${{ matrix.python }} - - name: Install dependencies - run: | - python -m pip install --upgrade pip - pip install pytest cffi - - - name: Build - run: | - pip install -e . - - name: Cache Mono if: runner.os == 'Windows' uses: actions/cache@v2 @@ -47,6 +66,22 @@ jobs: run: | choco install -y mono ${{ matrix.python == 'pypy3' && '--x86' || '' }} + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install pytest + + - name: Download wheel + uses: actions/download-artifact@v3 + with: + name: wheel + path: dist/ + + - name: Install wheel + shell: bash + run: | + pip install dist/*.whl + - name: Test with pytest run: | pytest diff --git a/pyproject.toml b/pyproject.toml index d344968..5491c06 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,12 +1,12 @@ [build-system] -requires = ["setuptools>=61", "wheel"] +requires = ["setuptools>=61", "setuptools_scm[toml]", "wheel"] build-backend = "setuptools.build_meta" [project] name = "clr_loader" description = "Generic pure Python loader for .NET runtimes" license = {text = "MIT"} -version = "0.2.3" +requires-python = ">=3.7" readme = "README.md" @@ -22,6 +22,8 @@ classifiers = [ "Operating System :: MacOS :: MacOS X", ] +dynamic = ["version"] + [[project.authors]] name = "Benedikt Reinartz" email = "filmor@gmail.com" @@ -37,6 +39,8 @@ package-data = {"clr_loader.ffi" = ["dlls/x86/*.dll", "dlls/amd64/*.dll"]} [tool.setuptools.packages.find] include = ["clr_loader*"] +[tool.setuptools_scm] + [tool.pytest.ini_options] xfail_strict = true testpaths = [ From 690a756757a41ab6c1e951655079ad148c89b415 Mon Sep 17 00:00:00 2001 From: Benedikt Reinartz Date: Sat, 15 Oct 2022 09:39:16 +0200 Subject: [PATCH 10/10] Add deployment to CI --- .github/workflows/ci.yml | 42 +++++++++++++++++++++++++++++++--------- 1 file changed, 33 insertions(+), 9 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index b4e25f9..45d1f06 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -23,14 +23,8 @@ jobs: - name: Upload source distribution uses: actions/upload-artifact@v3 with: - name: sdist - path: "dist/*.tar.gz" - if-no-files-found: error - - name: Upload wheel - uses: actions/upload-artifact@v3 - with: - name: wheel - path: "dist/*.whl" + name: build-output + path: "dist/*" if-no-files-found: error test: @@ -74,7 +68,7 @@ jobs: - name: Download wheel uses: actions/download-artifact@v3 with: - name: wheel + name: build-output path: dist/ - name: Install wheel @@ -85,3 +79,33 @@ jobs: - name: Test with pytest run: | pytest + + deploy: + runs-on: ubuntu-latest + needs: [build, test] + + steps: + - name: Download artifacts + uses: actions/download-artifact@v3 + with: + name: build-output + path: dist/ + + - name: Deploy to Test PyPI + uses: pypa/gh-action-pypi-publish@release/v1 + if: startsWith(github.ref, 'refs/head/master') + with: + password: ${{ secrets.TEST_PYPI_API_TOKEN }} + repository_url: https://test.pypi.org/legacy/ + + - name: Release + uses: softprops/action-gh-release@v1 + if: startsWith(github.ref, 'refs/tags/v') + with: + files: dist/* + + - name: Deploy to PyPI + uses: pypa/gh-action-pypi-publish@release/v1 + if: startsWith(github.ref, 'refs/tags/v') + with: + password: ${{ secrets.PYPI_API_TOKEN }}