diff --git a/.bumpversion.cfg b/.bumpversion.cfg deleted file mode 100644 index 96c5bfc..0000000 --- a/.bumpversion.cfg +++ /dev/null @@ -1,17 +0,0 @@ -[bumpversion] -current_version = 0.2.0 -message = Bump version to {new_version} -commit = True -tag = True - -[bumpversion:file:docs/conf.py] -search = release = '{current_version}' -replace = release = '{new_version}' - -[bumpversion:file:src/compas_nurbs/__version__.py] -search = __version__ = '{current_version}' -replace = __version__ = '{new_version}' - -[bumpversion:file:CHANGELOG.rst] -search = Unreleased -replace = {new_version} diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index ccff15f..e55ecdd 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -9,34 +9,17 @@ on: - main jobs: - build-packages: + build: + if: "!contains(github.event.pull_request.labels.*.name, 'docs-only')" runs-on: ${{ matrix.os }} strategy: matrix: os: [ubuntu-latest, macos-latest, windows-latest] - python-version: [3.6, 3.7, 3.8] + python: ["3.10", "3.11", "3.12"] steps: - - uses: actions/checkout@v2 - - - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v2 + - uses: compas-dev/compas-actions.build@v4 with: - python-version: ${{ matrix.python-version }} - - name: Install dependencies - run: | - python -m pip install --upgrade pip - python -m pip install wheel - python -m pip install cython --install-option="--no-cython-compile" - - name: Install - run: | - pip install --no-cache-dir -r requirements-dev.txt - - name: Lint with flake8 - run: | - invoke lint - - name: Test import - run: | - python -c "import compas_nurbs;print(compas_nurbs.__version__)" - - name: Test with pytest - run: | - invoke test + python: ${{ matrix.python }} + invoke_lint: true + invoke_test: true diff --git a/.github/workflows/cleanup.yml b/.github/workflows/cleanup.yml deleted file mode 100644 index 5c9dcd2..0000000 --- a/.github/workflows/cleanup.yml +++ /dev/null @@ -1,45 +0,0 @@ -name: cleanup - -# triggered whenever a PR submitted to main is merged or closed -on: - pull_request: - types: [closed] - branches: - - main - -jobs: - build: - name: cleanup - runs-on: ubuntu-latest - env: - PULL_FOLDER: pull_${{ github.event.pull_request.number }} - steps: - # checkout at gh-pages branch - - uses: actions/checkout@v2 - with: - ref: gh-pages - - # check if folder for this pull request exists - - name: Check folder existence - id: check_files - uses: andstor/file-existence-action@v1 - with: - files: ${{ env.PULL_FOLDER }} - - # delete the corresponding pull request doc folder - - name: Delete folder - if: steps.check_files.outputs.files_exists == 'true' - run: | - echo the folder $PULL_FOLDER will be deleted - if [ -d "$PULL_FOLDER" ]; then rm -Rf $PULL_FOLDER; fi - - # force push the deletion to gp-pages - - name: Deploy docs - if: success() && steps.check_files.outputs.files_exists == 'true' - uses: crazy-max/ghaction-github-pages@v2 - with: - target_branch: gh-pages - build_dir: ./ - keep_history: false - env: - GH_PAT: ${{ secrets.GH_PAT }} diff --git a/.github/workflows/deploy-n-publish.yml b/.github/workflows/deploy-n-publish.yml deleted file mode 100644 index b485b01..0000000 --- a/.github/workflows/deploy-n-publish.yml +++ /dev/null @@ -1,109 +0,0 @@ -name: docs - -on: - push: - branches: - - main - tags: - - 'v*' - pull_request: - branches: - - main - -jobs: - build: - name: build and deploy docs - runs-on: ubuntu-latest - env: - GH_PAT: ${{secrets.GH_PAT}} - steps: - - uses: actions/checkout@v2 - - name: Set up Python 3.8 - uses: actions/setup-python@v2 - with: - python-version: 3.8 - - name: 🔗 Install dependencies - run: | - python -m pip install --upgrade pip - python -m pip install wheel - python -m pip install cython --install-option="--no-cython-compile" - - name: 💎 Install - run: | - python -m pip install --no-cache-dir -r requirements-dev.txt - - name: 📃 Generate docs - if: ${{ success() }} - run: | - invoke docs - - # Get branch/tag/latest name from git - GITHUB_REF_REGEX="tags/v[0-9]{1,2}\.[0-9]{1,2}\.[0-9]{1,2}|(pull/[0-9]+)|heads/main" - if [[ $GITHUB_REF =~ $GITHUB_REF_REGEX ]]; then - if [[ $BASH_REMATCH = pull* ]]; then - echo This is a pull request - FOLDER_NAME=pull_${BASH_REMATCH##*/} - elif [[ $BASH_REMATCH = tags/* ]]; then - echo This is a version tag - FOLDER_NAME=${BASH_REMATCH##*/v} - else - echo This is a commit to main branch - FOLDER_NAME=latest - fi; - fi; - echo "Docs will be deployed to https://compas.dev/compas_nurbs/$FOLDER_NAME" - mkdir -p deploy/$BRANCH_OR_TAG && mv -T dist/docs deploy/$FOLDER_NAME/ - - - name: 🚢 Deploy docs - if: success() && env.GH_PAT != '' - uses: crazy-max/ghaction-github-pages@v2 - with: - target_branch: gh-pages - build_dir: deploy - keep_history: true - env: - GH_PAT: ${{ secrets.GH_PAT }} - - - name: 💃 Build release - if: success() && startsWith(github.ref, 'refs/tags') - run: | - python setup.py clean --all sdist bdist_wheel - - name: 📦 Publish release to PyPI - if: success() && startsWith(github.ref, 'refs/tags') - uses: pypa/gh-action-pypi-publish@master - with: - password: ${{ secrets.pypi_password }} - - docVersions: - needs: build - name: update doc versions - runs-on: ubuntu-latest - env: - GH_PAT: ${{secrets.GH_PAT}} - steps: - - - uses: actions/checkout@v2 - with: - ref: gh-pages - - - name: update doc versions - run: | - >doc_versions.txt - for folder in $(ls -rd */ | tr -d '/') - do - if [[ $folder =~ ^[0-9]{1,2}\.[0-9]{1,2}\.[0-9]{1,2}$|^latest$ ]] - then - echo ${folder} >> doc_versions.txt - fi - done - echo found available doc versions: - cat doc_versions.txt - - - name: Deploy docs - if: success() && env.GH_PAT != '' - uses: crazy-max/ghaction-github-pages@v2 - with: - target_branch: gh-pages - build_dir: ./ - keep_history: false - env: - GH_PAT: ${{ secrets.GH_PAT }} - diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml new file mode 100644 index 0000000..219f923 --- /dev/null +++ b/.github/workflows/docs.yml @@ -0,0 +1,19 @@ +name: docs + +on: + push: + branches: + - main + tags: + - 'v*' + pull_request: + branches: + - main + +jobs: + docs: + runs-on: ubuntu-latest + steps: + - uses: compas-dev/compas-actions.docs@v4 + with: + github_token: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/pr-checks.yml b/.github/workflows/pr-checks.yml index 6f10bb2..54433ed 100644 --- a/.github/workflows/pr-checks.yml +++ b/.github/workflows/pr-checks.yml @@ -4,6 +4,7 @@ on: types: [assigned, opened, synchronize, reopened, labeled, unlabeled] branches: - main + - master jobs: build: @@ -12,8 +13,9 @@ jobs: steps: - uses: actions/checkout@v1 - name: Changelog check - uses: Zomzog/changelog-checker@v1.1.0 + uses: Zomzog/changelog-checker@v1.2.0 with: fileName: CHANGELOG.rst + checkNotification: Simple env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000..3b34881 --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,30 @@ +name: release + +on: + push: + tags: + - 'v*' + +jobs: + build: + runs-on: ${{ matrix.os }} + strategy: + matrix: + os: [ubuntu-latest, macos-latest, windows-latest] + python: ['3.9', '3.10', '3.11'] + + steps: + - uses: compas-dev/compas-actions.build@v4 + with: + python: ${{ matrix.python }} + invoke_lint: true + invoke_test: true + + Publish: + needs: build + runs-on: ubuntu-latest + steps: + - uses: compas-dev/compas-actions.publish@v3 + with: + pypi_token: ${{ secrets.PYPI }} + github_token: ${{ secrets.GITHUB_TOKEN }} diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 882a7e3..40a25e7 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -11,12 +11,24 @@ Unreleased **Changed** +* Migrated from ``setup.py``/``setup.cfg`` to ``pyproject.toml`` (PEP 517/518) +* Updated CI workflows to use ``compas-dev/compas-actions@v4`` +* Updated tooling to use ``ruff`` for linting (replaced flake8, autopep8, isort, pydocstyle, pylint) +* Updated to ``compas_invocations2`` for invoke tasks +* Updated to ``sphinx_compas2_theme`` for documentation +* Updated repository URLs from gramaziokohler to compas-dev organization + **Fixed** +* Fixed ambiguous variable name in test_surface.py (E741) + **Deprecated** **Removed** +* Removed deprecated files: ``setup.py``, ``setup.cfg``, ``.bumpversion.cfg``, ``pytest.ini``, ``MANIFEST.in`` +* Removed ``__version__.py`` (version now defined in ``__init__.py``) + 0.2.1 ---------- **Changed** diff --git a/CONTRIBUTING.rst b/CONTRIBUTING.rst index 556acc6..7feaf2e 100644 --- a/CONTRIBUTING.rst +++ b/CONTRIBUTING.rst @@ -8,7 +8,7 @@ Code contributions We love pull requests from everyone! Here's a quick guide to improve the code: -1. Fork `the repository `_ and clone the fork. +1. Fork `the repository `_ and clone the fork. 2. Create a virtual environment using your tool of choice (e.g. ``virtualenv``, ``conda``, etc). 3. Install development dependencies: @@ -61,7 +61,7 @@ Once you made the documentation changes locally, run the documentation generatio Bug reports ----------- -When `reporting a bug `_ +When `reporting a bug `_ please include: * Operating system name and version. @@ -73,7 +73,7 @@ Feature requests and feedback ----------------------------- The best way to send feedback is to file an issue on -`Github `_. If you are proposing a feature: +`Github `_. If you are proposing a feature: * Explain in detail how it would work. * Keep the scope as narrow as possible, to make it easier to implement. diff --git a/MANIFEST.in b/MANIFEST.in deleted file mode 100644 index efedf7b..0000000 --- a/MANIFEST.in +++ /dev/null @@ -1,18 +0,0 @@ -graft docs -graft src -graft tests - -prune .github - -include .bumpversion.cfg -include .editorconfig - -include AUTHORS.rst -include CHANGELOG.rst -include CONTRIBUTING.rst -include LICENSE -include README.rst - -include requirements-dev.txt tasks.py pytest.ini - -global-exclude *.py[cod] __pycache__ *.so *.dylib diff --git a/README.rst b/README.rst index 493ecc0..da1af65 100644 --- a/README.rst +++ b/README.rst @@ -5,11 +5,11 @@ NURBS for COMPAS .. start-badges .. image:: https://img.shields.io/badge/License-MIT-blue.svg - :target: https://github.com/gramaziokohler/compas_nurbs/blob/main/LICENSE + :target: https://github.com/compas-dev/compas_nurbs/blob/main/LICENSE :alt: License MIT -.. image:: https://github.com/gramaziokohler/compas_nurbs/workflows/build/badge.svg - :target: https://github.com/gramaziokohler/compas_nurbs/actions +.. image:: https://github.com/compas-dev/compas_nurbs/workflows/build/badge.svg + :target: https://github.com/compas-dev/compas_nurbs/actions :alt: Github Actions .. end-badges @@ -24,7 +24,7 @@ Please refer to the Documentation_ for details. .. _NURBS-Python: https://github.com/orbingol/NURBS-Python .. _NumPy: https://numpy.org/ -.. _Documentation: https://gramaziokohler.github.io/compas_nurbs/latest/ +.. _Documentation: https://compas.dev/compas_nurbs/latest/ **COMPAS NURBS** runs on Python x.x and x.x. @@ -56,7 +56,7 @@ Contributing Make sure you setup your local development environment correctly: -* Clone the `compas_nurbs `_ repository. +* Clone the `compas_nurbs `_ repository. * Install development dependencies and make the project accessible from Rhino: :: diff --git a/docs/conf.py b/docs/conf.py index cab5a2d..b2f9cbf 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -1,55 +1,63 @@ # -*- coding: utf-8 -*- from sphinx.ext.napoleon.docstring import NumpyDocstring +from sphinx.writers import html, html5 -import sphinx_compas_theme +import sphinx_compas2_theme # -- General configuration ------------------------------------------------ project = 'COMPAS NURBS' copyright = '2020 - ETH Zurich, Gramazio Kohler Research' author = 'Romana Rust' -release = '0.2.0' - -version = '.'.join(release.split('.')[0:2]) +package = 'compas_nurbs' +organization = 'compas-dev' master_doc = 'index' -source_suffix = ['.rst', ] -templates_path = ['_templates'] -exclude_patterns = [] - -pygments_style = 'sphinx' -show_authors = True +source_suffix = {".rst": "restructuredtext"} +templates_path = sphinx_compas2_theme.get_autosummary_templates_path() +exclude_patterns = sphinx_compas2_theme.default_exclude_patterns add_module_names = True -language = None +language = "en" +# Version info +release = "0.2.0" +version = ".".join(release.split(".")[0:2]) # -- Extension configuration ------------------------------------------------ -extensions = [ - 'sphinx.ext.autodoc', - 'sphinx.ext.autosummary', - 'sphinx.ext.doctest', - 'sphinx.ext.intersphinx', - 'sphinx.ext.mathjax', - 'sphinx.ext.napoleon', - 'sphinx.ext.viewcode', - 'matplotlib.sphinxext.plot_directive', -] +extensions = sphinx_compas2_theme.default_extensions + +# numpydoc options + +numpydoc_show_class_members = False +numpydoc_class_members_toctree = False +numpydoc_attributes_as_param_list = True # autodoc options +autodoc_type_aliases = {} + +autodoc_typehints = "description" +autodoc_typehints_format = "short" +autodoc_typehints_description_target = "documented" + autodoc_mock_imports = ["Rhino", "System", "scriptcontext", "rhinoscriptsyntax", "clr", "bpy"] autodoc_default_options = { - 'undoc-members': True, - 'show-inheritance': True, + "undoc-members": True, + "show-inheritance": True, } -autodoc_member_order = 'groupwise' +autodoc_member_order = "groupwise" autoclass_content = "class" + +def setup(app): + app.connect("autodoc-skip-member", sphinx_compas2_theme.skip) + + # autosummary options autosummary_generate = True @@ -103,47 +111,82 @@ def patched_parse(self): NumpyDocstring._unpatched_parse = NumpyDocstring._parse NumpyDocstring._parse = patched_parse -# plot options - -# plot_include_source -# plot_pre_code -# plot_basedir -# plot_formats -# plot_rcparams -# plot_apply_rcparams -# plot_working_directory -# plot_template - -plot_html_show_source_link = False -plot_html_show_formats = False - # intersphinx options intersphinx_mapping = { - 'python': ('https://docs.python.org/', None), - 'compas': ('https://compas.dev/compas/latest/', None), + "python": ("https://docs.python.org/", None), + "compas": ("https://compas.dev/compas/latest/", None), } +# linkcode + +linkcode_resolve = sphinx_compas2_theme.get_linkcode_resolve(organization, package) + +# from pytorch + +sphinx_compas2_theme.replace(html.HTMLTranslator) +sphinx_compas2_theme.replace(html5.HTML5Translator) # -- Options for HTML output ---------------------------------------------- -html_theme = 'compaspkg' -html_theme_path = sphinx_compas_theme.get_html_theme_path() +html_theme = "sidebaronly" +html_title = project + +favicons = [ + { + "rel": "icon", + "href": "compas.ico", + } +] + html_theme_options = { - "package_name": "compas_nurbs", - "package_title": project, - "package_version": release, - "package_author": "Romana Rust", - "package_description": "COMPAS package for working with NURBS", - "package_repo": "https://github.com/gramaziokohler/compas_nurbs", - "package_docs": "https://gramaziokohler.github.io/compas_nurbs" + "icon_links": [ + { + "name": "GitHub", + "url": f"https://github.com/{organization}/{package}", + "icon": "fa-brands fa-github", + "type": "fontawesome", + }, + { + "name": "Discourse", + "url": "http://forum.compas-framework.org/", + "icon": "fa-brands fa-discourse", + "type": "fontawesome", + }, + { + "name": "PyPI", + "url": f"https://pypi.org/project/{package}/", + "icon": "fa-brands fa-python", + "type": "fontawesome", + }, + ], + "switcher": { + "json_url": f"https://raw.githubusercontent.com/{organization}/{package}/gh-pages/versions.json", + "version_match": version, + }, + "check_switcher": False, + "logo": { + "image_light": "_static/compas_icon_white.png", + "image_dark": "_static/compas_icon_white.png", + "text": project, + }, + "navigation_depth": 3, } -html_context = {} -html_static_path = [] -html_extra_path = ['.nojekyll'] -html_last_updated_fmt = '' + +html_context = { + "github_url": "https://github.com", + "github_user": organization, + "github_repo": package, + "github_version": "main", + "doc_path": "docs", +} + +html_static_path = sphinx_compas2_theme.get_html_static_path() + ["_static"] +html_css_files = [] +html_extra_path = [] +html_last_updated_fmt = "" html_copy_source = False -html_show_sourcelink = False -html_add_permalinks = '' -html_experimental_html5_writer = True +html_show_sourcelink = True +html_permalinks = False +html_permalinks_icon = "" html_compact_lists = True diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..f73a1dc --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,134 @@ +[build-system] +requires = ["setuptools>=66.0"] +build-backend = "setuptools.build_meta" + +# ============================================================================ +# project info +# ============================================================================ + +[project] +name = "compas_nurbs" +description = "NURBS for COMPAS" +keywords = ["compas", "nurbs", "rhino", "bspline"] +authors = [ + { name = "Gramazio Kohler Research", email = "gramaziokohler@arch.ethz.ch" }, +] +license = { file = "LICENSE" } +readme = "README.rst" +requires-python = ">=3.9" +dynamic = ['dependencies', 'optional-dependencies', 'version'] +classifiers = [ + "Development Status :: 3 - Alpha", + "Intended Audience :: Developers", + "Intended Audience :: Science/Research", + "License :: OSI Approved :: MIT License", + "Operating System :: Unix", + "Operating System :: POSIX", + "Operating System :: Microsoft :: Windows", + "Programming Language :: Python", + "Programming Language :: Python :: 3", + "Programming Language :: Python :: 3.9", + "Programming Language :: Python :: 3.10", + "Programming Language :: Python :: 3.11", + "Programming Language :: Python :: 3.12", + "Topic :: Scientific/Engineering", +] + +[project.urls] +Homepage = "https://compas.dev/compas_nurbs" +Documentation = "https://compas.dev/compas_nurbs" +Repository = "https://github.com/compas-dev/compas_nurbs" +Changelog = "https://github.com/compas-dev/compas_nurbs/blob/main/CHANGELOG.rst" +Issues = "https://github.com/compas-dev/compas_nurbs/issues" +Forum = "https://forum.compas-framework.org/" + +# ============================================================================ +# setuptools config +# ============================================================================ + +[tool.setuptools] +package-dir = { "" = "src" } +include-package-data = true +zip-safe = false + +[tool.setuptools.dynamic] +version = { attr = "compas_nurbs.__version__" } +dependencies = { file = "requirements.txt" } +optional-dependencies = { dev = { file = "requirements-dev.txt" } } + +[tool.setuptools.packages.find] +where = ["src"] + +# ============================================================================ +# replace pytest.ini +# ============================================================================ + +[tool.pytest.ini_options] +minversion = "6.0" +testpaths = ["tests", "src/compas_nurbs"] +python_files = ["test_*.py", "*_test.py", "test.py"] +addopts = [ + "-ra", + "--strict-markers", + "--doctest-glob=*.rst", + "--tb=short", + "--import-mode=importlib", +] +doctest_optionflags = [ + "NORMALIZE_WHITESPACE", + "IGNORE_EXCEPTION_DETAIL", + "ALLOW_UNICODE", + "ALLOW_BYTES", + "NUMBER", +] + +# ============================================================================ +# replace bumpversion.cfg +# ============================================================================ + +[tool.bumpversion] +current_version = "0.2.0" +message = "Bump version to {new_version}" +commit = true +tag = true + +[[tool.bumpversion.files]] +filename = "src/compas_nurbs/__init__.py" +search = "__version__ = \"{current_version}\"" +replace = "__version__ = \"{new_version}\"" + +[[tool.bumpversion.files]] +filename = "CHANGELOG.rst" +search = "Unreleased" +replace = "{new_version}" + +# ============================================================================ +# replace setup.cfg +# ============================================================================ + +[tool.ruff] +line-length = 179 +indent-width = 4 +target-version = "py39" + +[tool.ruff.lint] +select = ["E", "F", "I"] + +[tool.ruff.lint.per-file-ignores] +"__init__.py" = ["I001"] +"tests/*" = ["I001"] +"tasks.py" = ["I001"] + +[tool.ruff.lint.isort] +force-single-line = true +known-first-party = ["compas_nurbs"] + +[tool.ruff.lint.pydocstyle] +convention = "numpy" + +[tool.ruff.lint.pycodestyle] +max-doc-length = 179 + +[tool.ruff.format] +docstring-code-format = true +docstring-code-line-length = "dynamic" diff --git a/pytest.ini b/pytest.ini deleted file mode 100644 index 23ce1a5..0000000 --- a/pytest.ini +++ /dev/null @@ -1,3 +0,0 @@ -[pytest] -testpaths = tests src -doctest_optionflags= NORMALIZE_WHITESPACE IGNORE_EXCEPTION_DETAIL ALLOW_UNICODE ALLOW_BYTES diff --git a/requirements-dev.txt b/requirements-dev.txt index 8d2300b..1b5e21f 100644 --- a/requirements-dev.txt +++ b/requirements-dev.txt @@ -1,15 +1,13 @@ -autopep8 -bump2version >=0.5.11 -check-manifest >=0.36 -doc8 -flake8 -geomdl # Used only for testing +attrs >=17.4 +build +bump-my-version +compas_invocations2 +geomdl invoke >=0.14 -isort -pydocstyle -pylint pytest -rhino3dm>=8.17 # Used only for testing +rhino3dm>=8.17 +ruff sphinx >=1.6 -sphinx_compas_theme >=0.13 --e . +sphinx_compas2_theme +twine +wheel diff --git a/setup.cfg b/setup.cfg deleted file mode 100644 index dfb6d12..0000000 --- a/setup.cfg +++ /dev/null @@ -1,37 +0,0 @@ -[bdist_wheel] -universal = 1 - -[flake8] -max-line-length = 180 -exclude = */migrations/* - -[doc8] -max-line-length = 180 -ignore = D001 - -[pydocstyle] -convention = numpy - -[tool:pytest] -testpaths = tests -norecursedirs = - migrations -python_files = - test_*.py - *_test.py - tests.py -addopts = - -ra - --strict - --doctest-modules - --doctest-glob=\*.rst - --tb=short - -[isort] -force_single_line = True -line_length = 180 -known_first_party = compas_nurbs -default_section = THIRDPARTY -forced_separate = test_compas_nurbs -not_skip = __init__.py -skip = migrations diff --git a/setup.py b/setup.py deleted file mode 100644 index a60c976..0000000 --- a/setup.py +++ /dev/null @@ -1,85 +0,0 @@ -#!/usr/bin/env python -# -*- encoding: utf-8 -*- -from __future__ import absolute_import, print_function -import io -import re -from glob import glob -from os.path import abspath, basename, dirname, join, splitext - -from setuptools import find_packages, setup - -from pathlib import Path - -here = Path(__file__).parent - -def read_requirements(filename: str) -> list[str]: - req_path = here / filename - if not req_path.exists(): - return [] - lines = req_path.read_text(encoding="utf-8").splitlines() - reqs = [] - for line in lines: - s = line.strip() - # skip comments/empties and nested includes - if not s or s.startswith("#") or s.startswith(("-r ", "--requirement ")): - continue - reqs.append(s) - return reqs - - -keywords_list = ['compas', 'nurbs', 'rhino', 'bspline'] - -here = abspath(dirname(__file__)) - - -def read(*names, **kwargs): - return io.open( - join(here, *names), - encoding=kwargs.get('encoding', 'utf8') - ).read() - - -about = {} -exec(read('src', 'compas_nurbs', '__version__.py'), about) - -setup( - name=about['__title__'], - version=about['__version__'], - license=about['__license__'], - description=about['__description__'], - author=about['__author__'], - author_email=about['__author_email__'], - url=about['__url__'], - long_description='%s\n%s' % ( - re.compile('^.. start-badges.*^.. end-badges', re.M | - re.S).sub('', read('README.rst')), - re.sub(':[a-z]+:`~?(.*?)`', r'``\1``', read('CHANGELOG.rst')) - ), - packages=find_packages('src'), - package_dir={'': 'src'}, - py_modules=[splitext(basename(path))[0] for path in glob('src/*.py')], - include_package_data=True, - zip_safe=False, - classifiers=[ - 'Development Status :: 3 - Alpha', - 'Intended Audience :: Developers', - 'Intended Audience :: Science/Research', - 'License :: OSI Approved :: MIT License', - 'Operating System :: Unix', - 'Operating System :: POSIX', - 'Operating System :: Microsoft :: Windows', - 'Programming Language :: Python', - 'Programming Language :: Python :: 3', - 'Programming Language :: Python :: 3.4', - 'Programming Language :: Python :: 3.5', - 'Programming Language :: Python :: 3.6', - 'Programming Language :: Python :: 3.7', - 'Programming Language :: Python :: 3.8', - 'Programming Language :: Python :: Implementation :: CPython', - 'Topic :: Scientific/Engineering', - ], - keywords=keywords_list, - install_requires=read_requirements("requirements.txt"), - extras_require={}, - entry_points={}, -) diff --git a/src/compas_nurbs/__init__.py b/src/compas_nurbs/__init__.py index d1cfadb..0f97de2 100644 --- a/src/compas_nurbs/__init__.py +++ b/src/compas_nurbs/__init__.py @@ -34,12 +34,13 @@ import os -from .__version__ import __version__ from .curve import Curve from .curve import RationalCurve # noqa: F401 from .surface import RationalSurface from .surface import Surface +__version__ = "0.2.0" + HERE = os.path.dirname(__file__) DATA = os.path.join(HERE, "data") diff --git a/src/compas_nurbs/__version__.py b/src/compas_nurbs/__version__.py deleted file mode 100644 index 54027e6..0000000 --- a/src/compas_nurbs/__version__.py +++ /dev/null @@ -1,10 +0,0 @@ -__title__ = 'compas_nurbs' -__description__ = 'NURBS for COMPAS' -__url__ = 'https://github.com/gramaziokohler/compas_nurbs' -__version__ = '0.2.0' -__author__ = 'Gramazio Kohler Research' -__author_email__ = 'gramaziokohler@arch.ethz.ch' -__license__ = 'MIT license' -__copyright__ = 'Copyright 2020 ETH Zurich, Gramazio Kohler Research' - -__all__ = ['__author__', '__author_email__', '__copyright__', '__description__', '__license__', '__title__', '__url__', '__version__'] diff --git a/src/compas_nurbs/conftest.py b/src/compas_nurbs/conftest.py index 9fc2d66..4fec26c 100644 --- a/src/compas_nurbs/conftest.py +++ b/src/compas_nurbs/conftest.py @@ -1,8 +1,9 @@ import pytest -from compas.geometry import close from compas.geometry import allclose -from compas_nurbs import Surface +from compas.geometry import close + from compas_nurbs import Curve +from compas_nurbs import Surface @pytest.fixture(autouse=True) diff --git a/src/compas_nurbs/curvature.py b/src/compas_nurbs/curvature.py index e57030f..5f03b8e 100644 --- a/src/compas_nurbs/curvature.py +++ b/src/compas_nurbs/curvature.py @@ -1,7 +1,7 @@ -from compas.geometry import Vector from compas.geometry import Circle from compas.geometry import Plane +from compas.geometry import Vector class CurveCurvature(object): diff --git a/src/compas_nurbs/curve.py b/src/compas_nurbs/curve.py index e60200a..6e8c091 100644 --- a/src/compas_nurbs/curve.py +++ b/src/compas_nurbs/curve.py @@ -1,8 +1,7 @@ import compas - +from compas.geometry import Frame from compas.geometry import Point from compas.geometry import Vector -from compas.geometry import Frame from compas_nurbs.bspline import BSpline from compas_nurbs.curvature import CurveCurvature @@ -11,10 +10,10 @@ from compas_nurbs.evaluators import create_curve from compas_nurbs.evaluators import evaluate_curve from compas_nurbs.evaluators import evaluate_curve_derivatives - from compas_nurbs.operations import curve_tangents - from compas_nurbs.operations import curve_frames - from compas_nurbs.operations import curve_curvatures from compas_nurbs.fitting import interpolate_curve + from compas_nurbs.operations import curve_curvatures + from compas_nurbs.operations import curve_frames + from compas_nurbs.operations import curve_tangents class Curve(BSpline): @@ -263,6 +262,7 @@ def weighted_control_points(self): if __name__ == '__main__': import doctest + from compas.geometry import allclose # noqa: F401 from compas.geometry import close # noqa: F401 control_points = [(0, 0, 0), (3, 4, 0), (-1, 4, 0), (-4, 0, 0), (-4, -3, 0)] diff --git a/src/compas_nurbs/evaluators.py b/src/compas_nurbs/evaluators.py index 41c9c65..7c44992 100644 --- a/src/compas_nurbs/evaluators.py +++ b/src/compas_nurbs/evaluators.py @@ -1,10 +1,10 @@ -import scipy import numpy as np +import scipy from geomdl.linalg import binomial_coefficient -from .helpers import find_spans from .helpers import basis_functions from .helpers import basis_functions_derivatives +from .helpers import find_spans # ============================================================================== # curve diff --git a/src/compas_nurbs/fitting.py b/src/compas_nurbs/fitting.py index 0b10e36..b8b0d4f 100644 --- a/src/compas_nurbs/fitting.py +++ b/src/compas_nurbs/fitting.py @@ -1,13 +1,12 @@ import numpy as np -from scipy.linalg import lu_factor, lu_solve - from compas.geometry import allclose # noqa: F401 +from scipy.linalg import lu_factor +from scipy.linalg import lu_solve -from compas_nurbs.helpers import find_spans from compas_nurbs.helpers import basis_functions -from compas_nurbs.knot_vectors import knot_vector_and_params +from compas_nurbs.helpers import find_spans from compas_nurbs.knot_vectors import CurveKnotStyle - +from compas_nurbs.knot_vectors import knot_vector_and_params # TODO: estimate derivatives for degree==3 # https://link.springer.com/content/pdf/10.1007/s003660050038.pdf diff --git a/src/compas_nurbs/ghpython/curve_artist.py b/src/compas_nurbs/ghpython/curve_artist.py index 3981208..003e6ae 100644 --- a/src/compas_nurbs/ghpython/curve_artist.py +++ b/src/compas_nurbs/ghpython/curve_artist.py @@ -1,5 +1,4 @@ import Rhino.Geometry as rg - from compas_rhino.artists import BaseArtist diff --git a/src/compas_nurbs/ghpython/geometry.py b/src/compas_nurbs/ghpython/geometry.py index 098b961..780df15 100644 --- a/src/compas_nurbs/ghpython/geometry.py +++ b/src/compas_nurbs/ghpython/geometry.py @@ -1,8 +1,9 @@ import compas + from compas_nurbs import Curve from compas_nurbs import RationalCurve -from compas_nurbs import Surface from compas_nurbs import RationalSurface +from compas_nurbs import Surface from compas_nurbs.utilities import reshape if compas.RHINO: diff --git a/src/compas_nurbs/ghpython/surface_artist.py b/src/compas_nurbs/ghpython/surface_artist.py index 832427f..a8df4d7 100644 --- a/src/compas_nurbs/ghpython/surface_artist.py +++ b/src/compas_nurbs/ghpython/surface_artist.py @@ -1,6 +1,5 @@ import Rhino.Geometry as rg import rhinoscriptsyntax as rs - from compas.utilities import flatten from compas_rhino.artists import BaseArtist diff --git a/src/compas_nurbs/knot_vectors.py b/src/compas_nurbs/knot_vectors.py index 9cf104c..10d43e8 100644 --- a/src/compas_nurbs/knot_vectors.py +++ b/src/compas_nurbs/knot_vectors.py @@ -1,6 +1,8 @@ import math + from compas.geometry import Bezier from compas.geometry import distance_point_point + from .helpers import EPSILON diff --git a/src/compas_nurbs/operations.py b/src/compas_nurbs/operations.py index da64541..a2f29ea 100644 --- a/src/compas_nurbs/operations.py +++ b/src/compas_nurbs/operations.py @@ -1,4 +1,5 @@ import numpy as np + from .helpers import EPSILON from .helpers import knotspan from .knot_vectors import knot_vector_multiplicities diff --git a/src/compas_nurbs/rhino/curve_artist.py b/src/compas_nurbs/rhino/curve_artist.py index 2330fca..113d357 100644 --- a/src/compas_nurbs/rhino/curve_artist.py +++ b/src/compas_nurbs/rhino/curve_artist.py @@ -3,7 +3,6 @@ import Rhino.Geometry as rg import rhinoscriptsyntax as rs import scriptcontext as sc - from compas_rhino.artists import BaseArtist diff --git a/src/compas_nurbs/rhino/surface_artist.py b/src/compas_nurbs/rhino/surface_artist.py index 30bbdad..5bc9779 100644 --- a/src/compas_nurbs/rhino/surface_artist.py +++ b/src/compas_nurbs/rhino/surface_artist.py @@ -3,7 +3,6 @@ import Rhino.Geometry as rg import rhinoscriptsyntax as rs import scriptcontext as sc - from compas.utilities import flatten from compas_rhino.artists import BaseArtist diff --git a/src/compas_nurbs/surface.py b/src/compas_nurbs/surface.py index dc5fdb9..d01a72f 100644 --- a/src/compas_nurbs/surface.py +++ b/src/compas_nurbs/surface.py @@ -1,17 +1,19 @@ import compas -from compas.geometry import Shape, Vector, Point +from compas.geometry import Point +from compas.geometry import Shape +from compas.geometry import Vector from compas_nurbs.bspline import BSpline -from compas_nurbs.curve import Curve from compas_nurbs.curvature import SurfaceCurvature +from compas_nurbs.curve import Curve if not compas.IPY: + from compas_nurbs.evaluators import calculate_surface_curvature from compas_nurbs.evaluators import evaluate_surface from compas_nurbs.evaluators import evaluate_surface_derivatives - from compas_nurbs.evaluators import calculate_surface_curvature + from compas_nurbs.operations import surface_isocurve from compas_nurbs.operations import surface_normals from compas_nurbs.operations import unify_curves - from compas_nurbs.operations import surface_isocurve class Surface(BSpline, Shape): @@ -238,6 +240,7 @@ def weighted_control_points(self): if __name__ == "__main__": import doctest + from compas.geometry import allclose # noqa: F401 from compas.geometry import close # noqa: F401 diff --git a/tasks.py b/tasks.py index 3307298..7065c9b 100644 --- a/tasks.py +++ b/tasks.py @@ -1,231 +1,27 @@ -# -*- coding: utf-8 -*- -from __future__ import print_function - -import contextlib -import glob import os -import sys -from shutil import rmtree - -from invoke import Exit -from invoke import task - -try: - input = raw_input -except NameError: - pass - - -BASE_FOLDER = os.path.dirname(__file__) - - -class Log(object): - def __init__(self, out=sys.stdout, err=sys.stderr): - self.out = out - self.err = err - - def flush(self): - self.out.flush() - self.err.flush() - - def write(self, message): - self.flush() - self.out.write(message + '\n') - self.out.flush() - - def info(self, message): - self.write('[INFO] %s' % message) - - def warn(self, message): - self.write('[WARN] %s' % message) - - -log = Log() - - -def confirm(question): - while True: - response = input(question).lower().strip() - - if not response or response in ('n', 'no'): - return False - - if response in ('y', 'yes'): - return True - - print('Focus, kid! It is either (y)es or (n)o', file=sys.stderr) - - -@task(default=True) -def help(ctx): - """Lists available tasks and usage.""" - ctx.run('invoke --list') - log.write('Use "invoke -h " to get detailed help for a task.') - - -@task(help={ - 'docs': 'True to generate documentation, otherwise False', - 'bytecode': 'True to clean up compiled python files, otherwise False.', - 'builds': 'True to clean up build/packaging artifacts, otherwise False.'}) -def clean(ctx, docs=True, bytecode=True, builds=True): - """Cleans the local copy from compiled artifacts.""" - if builds: - ctx.run('python setup.py clean') - - if bytecode: - for root, dirs, files in os.walk(BASE_FOLDER): - for f in files: - if f.endswith('.pyc'): - os.remove(os.path.join(root, f)) - if '.git' in dirs: - dirs.remove('.git') - - folders = [] - - if docs: - folders.append('docs/_build/') - folders.append('dist/') - - if bytecode: - folders.append('src/compas_nurbs/__pycache__') - - if builds: - folders.append('build/') - folders.append('src/compas_nurbs.egg-info/') - - for folder in folders: - rmtree(os.path.join(BASE_FOLDER, folder), ignore_errors=True) - - -@task(help={ - 'rebuild': 'True to clean all previously built docs before starting, otherwise False.', - 'doctest': 'True to run doctests, otherwise False.', - 'check_links': 'True to check all web links in docs for validity, otherwise False.'}) -def docs(ctx, rebuild=False, doctest=False, check_links=False): - """Builds package's HTML documentation.""" - if rebuild: - clean(ctx) - - with chdir(BASE_FOLDER): - if doctest: - testdocs(ctx, rebuild=rebuild) - - opts = '-E' if rebuild else '' - ctx.run('sphinx-build {} -b html docs dist/docs'.format(opts)) - - if check_links: - linkcheck(ctx, rebuild=rebuild) - - -@task() -def lint(ctx): - """Check the consistency of coding style.""" - log.write('Running flake8 python linter...') - ctx.run('flake8 src') - - -@task() -def testdocs(ctx, rebuild=False): - """Test the examples in the docstrings.""" - log.write('Running doctest...') - opts = '-E' if rebuild else '' - ctx.run('sphinx-build {} -b doctest docs dist/docs'.format(opts)) - - -@task() -def linkcheck(ctx, rebuild=False): - """Check links in documentation.""" - log.write('Running link check...') - opts = '-E' if rebuild else '' - ctx.run('sphinx-build {} -b linkcheck docs dist/docs'.format(opts)) - - -@task() -def check(ctx): - """Check the consistency of documentation, coding style and a few other things.""" - with chdir(BASE_FOLDER): - lint(ctx) - - log.write('Checking MANIFEST.in...') - ctx.run('check-manifest') - - log.write('Checking ReStructuredText formatting...') - ctx.run('python setup.py check --strict --metadata --restructuredtext') - - -@task(help={ - 'checks': 'True to run all checks before testing, otherwise False.'}) -def test(ctx, checks=False, doctest=False): - """Run all tests.""" - if checks: - check(ctx) - - with chdir(BASE_FOLDER): - cmd = ['pytest'] - if doctest: - cmd.append('--doctest-modules') - - ctx.run(' '.join(cmd)) - - -@task -def prepare_changelog(ctx): - """Prepare changelog for next release.""" - UNRELEASED_CHANGELOG_TEMPLATE = '\nUnreleased\n----------\n\n**Added**\n\n**Changed**\n\n**Fixed**\n\n**Deprecated**\n\n**Removed**\n' - - with chdir(BASE_FOLDER): - # Preparing changelog for next release - with open('CHANGELOG.rst', 'r+') as changelog: - content = changelog.read() - start_index = content.index('----------') - start_index = content.rindex('\n', 0, start_index - 1) - last_version = content[start_index:start_index + 11].strip() - - if last_version == 'Unreleased': - log.write('Already up-to-date') - return - - changelog.seek(0) - changelog.write(content[0:start_index] + UNRELEASED_CHANGELOG_TEMPLATE + content[start_index:]) - - ctx.run('git add CHANGELOG.rst && git commit -m "Prepare changelog for next release"') - - -@task(help={ - 'release_type': 'Type of release follows semver rules. Must be one of: major, minor, patch.'}) -def release(ctx, release_type): - """Releases the project in one swift command!""" - if release_type not in ('patch', 'minor', 'major'): - raise Exit('The release type parameter is invalid.\nMust be one of: major, minor, patch') - - # Run checks - ctx.run('invoke check test') - - # Bump version and git tag it - ctx.run('bump2version %s --verbose' % release_type) - - # Build project - ctx.run('python setup.py clean --all sdist bdist_wheel') - - # Prepare changelog for next release - prepare_changelog(ctx) - - # Clean up local artifacts - clean(ctx) - - # Upload to pypi - if confirm('Everything is ready. You are about to push to git which will trigger a release to pypi.org. Are you sure? [y/N]'): - ctx.run('git push --tags && git push') - else: - raise Exit('You need to manually revert the tag/commits created.') - -@contextlib.contextmanager -def chdir(dirname=None): - current_dir = os.getcwd() - try: - if dirname is not None: - os.chdir(dirname) - yield - finally: - os.chdir(current_dir) +from compas_invocations2 import build +from compas_invocations2 import docs +from compas_invocations2 import style +from compas_invocations2 import tests +from invoke.collection import Collection + +ns = Collection( + docs.help, + style.check, + style.lint, + style.format, + docs.docs, + docs.linkcheck, + tests.test, + tests.testdocs, + tests.testcodeblocks, + build.prepare_changelog, + build.clean, + build.release, +) +ns.configure( + { + "base_folder": os.path.dirname(__file__), + } +) diff --git a/tests/test_surface.py b/tests/test_surface.py index 608fb19..50ccb2e 100644 --- a/tests/test_surface.py +++ b/tests/test_surface.py @@ -32,8 +32,8 @@ def rhino_surface_from_surface(surface): count_u, count_v = surface.count knot_vector_u, knot_vector_v = surface.knot_vector srf_rhino = rhino3dm.NurbsSurface.Create(3, surface.rational, degree_u + 1, degree_v + 1, count_u, count_v) - for u, l in enumerate(surface.control_points): - for v, (x, y, z) in enumerate(l): + for u, row in enumerate(surface.control_points): + for v, (x, y, z) in enumerate(row): if surface.rational: srf_rhino.Points[(u, v)] = rhino3dm.Point4d(x, y, z, surface.weights[u][v]) else: