From c681f6748acaea1bf0b706528c36327cc94a6eed Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Tue, 8 Dec 2020 16:29:09 -0500 Subject: [PATCH 001/176] Collapse skeleton history from archive/2020-12 --- .coveragerc | 5 ++ .flake8 | 9 +++ .github/workflows/main.yml | 42 +++++++++++ .pre-commit-config.yaml | 10 +++ .readthedocs.yml | 6 ++ CHANGES.rst | 0 LICENSE | 19 +++++ README.rst | 18 +++++ docs/conf.py | 26 +++++++ docs/history.rst | 8 +++ docs/index.rst | 22 ++++++ mypy.ini | 2 + pyproject.toml | 22 ++++++ pytest.ini | 9 +++ setup.cfg | 45 ++++++++++++ setup.py | 6 ++ skeleton.md | 144 +++++++++++++++++++++++++++++++++++++ tox.ini | 40 +++++++++++ 18 files changed, 433 insertions(+) create mode 100644 .coveragerc create mode 100644 .flake8 create mode 100644 .github/workflows/main.yml create mode 100644 .pre-commit-config.yaml create mode 100644 .readthedocs.yml create mode 100644 CHANGES.rst create mode 100644 LICENSE create mode 100644 README.rst create mode 100644 docs/conf.py create mode 100644 docs/history.rst create mode 100644 docs/index.rst create mode 100644 mypy.ini create mode 100644 pyproject.toml create mode 100644 pytest.ini create mode 100644 setup.cfg create mode 100644 setup.py create mode 100644 skeleton.md create mode 100644 tox.ini diff --git a/.coveragerc b/.coveragerc new file mode 100644 index 0000000..4582306 --- /dev/null +++ b/.coveragerc @@ -0,0 +1,5 @@ +[run] +omit = .tox/* + +[report] +show_missing = True diff --git a/.flake8 b/.flake8 new file mode 100644 index 0000000..790c109 --- /dev/null +++ b/.flake8 @@ -0,0 +1,9 @@ +[flake8] +max-line-length = 88 +ignore = + # W503 violates spec https://github.com/PyCQA/pycodestyle/issues/513 + W503 + # W504 has issues https://github.com/OCA/maintainer-quality-tools/issues/545 + W504 + # Black creates whitespace before colon + E203 diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml new file mode 100644 index 0000000..8c5c232 --- /dev/null +++ b/.github/workflows/main.yml @@ -0,0 +1,42 @@ +name: Automated Tests + +on: [push, pull_request] + +jobs: + test: + strategy: + matrix: + python: [3.6, 3.8, 3.9] + platform: [ubuntu-latest, macos-latest, windows-latest] + runs-on: ${{ matrix.platform }} + steps: + - uses: actions/checkout@v2 + - name: Setup Python + uses: actions/setup-python@v2 + with: + python-version: ${{ matrix.python }} + - name: Install tox + run: | + python -m pip install tox + - name: Run tests + run: tox + + release: + needs: test + if: github.event_name == 'push' && contains(github.ref, 'refs/tags/') + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v2 + - name: Setup Python + uses: actions/setup-python@v2 + with: + python-version: 3.9 + - name: Install tox + run: | + python -m pip install tox + - name: Release + run: tox -e release + env: + TWINE_PASSWORD: ${{ secrets.PYPI_TOKEN }} + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml new file mode 100644 index 0000000..6639c78 --- /dev/null +++ b/.pre-commit-config.yaml @@ -0,0 +1,10 @@ +repos: +- repo: https://github.com/psf/black + rev: stable + hooks: + - id: black + +- repo: https://github.com/asottile/blacken-docs + rev: v1.8.0 + hooks: + - id: blacken-docs diff --git a/.readthedocs.yml b/.readthedocs.yml new file mode 100644 index 0000000..cc69854 --- /dev/null +++ b/.readthedocs.yml @@ -0,0 +1,6 @@ +version: 2 +python: + install: + - path: . + extra_requirements: + - docs diff --git a/CHANGES.rst b/CHANGES.rst new file mode 100644 index 0000000..e69de29 diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..353924b --- /dev/null +++ b/LICENSE @@ -0,0 +1,19 @@ +Copyright Jason R. Coombs + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to +deal in the Software without restriction, including without limitation the +rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +sell copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +IN THE SOFTWARE. diff --git a/README.rst b/README.rst new file mode 100644 index 0000000..69554ef --- /dev/null +++ b/README.rst @@ -0,0 +1,18 @@ +.. image:: https://img.shields.io/pypi/v/skeleton.svg + :target: `PyPI link`_ + +.. image:: https://img.shields.io/pypi/pyversions/skeleton.svg + :target: `PyPI link`_ + +.. _PyPI link: https://pypi.org/project/skeleton + +.. image:: https://github.com/jaraco/skeleton/workflows/Automated%20Tests/badge.svg + :target: https://github.com/jaraco/skeleton/actions?query=workflow%3A%22Automated+Tests%22 + :alt: Automated Tests + +.. image:: https://img.shields.io/badge/code%20style-black-000000.svg + :target: https://github.com/psf/black + :alt: Code style: Black + +.. .. image:: https://readthedocs.org/projects/skeleton/badge/?version=latest +.. :target: https://skeleton.readthedocs.io/en/latest/?badge=latest diff --git a/docs/conf.py b/docs/conf.py new file mode 100644 index 0000000..433d185 --- /dev/null +++ b/docs/conf.py @@ -0,0 +1,26 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- + +extensions = ['sphinx.ext.autodoc', 'jaraco.packaging.sphinx', 'rst.linker'] + +master_doc = "index" + +link_files = { + '../CHANGES.rst': dict( + using=dict(GH='https://github.com'), + replace=[ + dict( + pattern=r'(Issue #|\B#)(?P\d+)', + url='{package_url}/issues/{issue}', + ), + dict( + pattern=r'(?m:^((?Pv?\d+(\.\d+){1,2}))\n[-=]+\n)', + with_scm='{text}\n{rev[timestamp]:%d %b %Y}\n', + ), + dict( + pattern=r'PEP[- ](?P\d+)', + url='https://www.python.org/dev/peps/pep-{pep_number:0>4}/', + ), + ], + ) +} diff --git a/docs/history.rst b/docs/history.rst new file mode 100644 index 0000000..8e21750 --- /dev/null +++ b/docs/history.rst @@ -0,0 +1,8 @@ +:tocdepth: 2 + +.. _changes: + +History +******* + +.. include:: ../CHANGES (links).rst diff --git a/docs/index.rst b/docs/index.rst new file mode 100644 index 0000000..d14131b --- /dev/null +++ b/docs/index.rst @@ -0,0 +1,22 @@ +Welcome to skeleton documentation! +======================================== + +.. toctree:: + :maxdepth: 1 + + history + + +.. automodule:: skeleton + :members: + :undoc-members: + :show-inheritance: + + +Indices and tables +================== + +* :ref:`genindex` +* :ref:`modindex` +* :ref:`search` + diff --git a/mypy.ini b/mypy.ini new file mode 100644 index 0000000..976ba02 --- /dev/null +++ b/mypy.ini @@ -0,0 +1,2 @@ +[mypy] +ignore_missing_imports = True diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..79f088a --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,22 @@ +[build-system] +requires = ["setuptools>=42", "wheel", "setuptools_scm[toml]>=3.4.1"] +build-backend = "setuptools.build_meta" + +[tool.black] +skip-string-normalization = true + +[tool.setuptools_scm] + +# jaraco/skeleton#22 +[tool.jaraco.pytest.plugins.black] +addopts = "--black" + +# jaraco/skeleton#22 +[tool.jaraco.pytest.plugins.mypy] +addopts = "--mypy" + +[tool.jaraco.pytest.plugins.flake8] +addopts = "--flake8" + +[tool.jaraco.pytest.plugins.cov] +addopts = "--cov" diff --git a/pytest.ini b/pytest.ini new file mode 100644 index 0000000..d7f0b11 --- /dev/null +++ b/pytest.ini @@ -0,0 +1,9 @@ +[pytest] +norecursedirs=dist build .tox .eggs +addopts=--doctest-modules +doctest_optionflags=ALLOW_UNICODE ELLIPSIS +# workaround for warning pytest-dev/pytest#6178 +junit_family=xunit2 +filterwarnings= + # https://github.com/pytest-dev/pytest/issues/6928 + ignore:direct construction of .*Item has been deprecated:DeprecationWarning diff --git a/setup.cfg b/setup.cfg new file mode 100644 index 0000000..6321ca7 --- /dev/null +++ b/setup.cfg @@ -0,0 +1,45 @@ +[metadata] +license_file = LICENSE +name = skeleton +author = Jason R. Coombs +author_email = jaraco@jaraco.com +description = skeleton +long_description = file:README.rst +url = https://github.com/jaraco/skeleton +classifiers = + Development Status :: 5 - Production/Stable + Intended Audience :: Developers + License :: OSI Approved :: MIT License + Programming Language :: Python :: 3 + Programming Language :: Python :: 3 :: Only + +[options] +packages = find: +include_package_data = true +python_requires = >=3.6 +install_requires = +setup_requires = setuptools_scm[toml] >= 3.4.1 + +[options.extras_require] +testing = + # upstream + pytest >= 3.5, !=3.7.3 + pytest-checkdocs >= 1.2.3 + pytest-flake8 + pytest-black >= 0.3.7; python_implementation != "PyPy" + pytest-cov + pytest-mypy; python_implementation != "PyPy" + # jaraco/skeleton#22 + jaraco.test >= 3.2.0 + + # local + +docs = + # upstream + sphinx + jaraco.packaging >= 3.2 + rst.linker >= 1.9 + + # local + +[options.entry_points] diff --git a/setup.py b/setup.py new file mode 100644 index 0000000..bac24a4 --- /dev/null +++ b/setup.py @@ -0,0 +1,6 @@ +#!/usr/bin/env python + +import setuptools + +if __name__ == "__main__": + setuptools.setup() diff --git a/skeleton.md b/skeleton.md new file mode 100644 index 0000000..ec421c2 --- /dev/null +++ b/skeleton.md @@ -0,0 +1,144 @@ +# Overview + +This project is merged with [skeleton](https://github.com/jaraco/skeleton). What is skeleton? It's the scaffolding of a Python project jaraco [introduced in his blog](https://blog.jaraco.com/a-project-skeleton-for-python-projects/). It seeks to provide a means to re-use techniques and inherit advances when managing projects for distribution. + +## An SCM-Managed Approach + +While maintaining dozens of projects in PyPI, jaraco derives best practices for project distribution and publishes them in the [skeleton repo](https://github.com/jaraco/skeleton), a Git repo capturing the evolution and culmination of these best practices. + +It's intended to be used by a new or existing project to adopt these practices and honed and proven techniques. Adopters are encouraged to use the project directly and maintain a small deviation from the technique, make their own fork for more substantial changes unique to their environment or preferences, or simply adopt the skeleton once and abandon it thereafter. + +The primary advantage to using an SCM for maintaining these techniques is that those tools help facilitate the merge between the template and its adopting projects. + +Another advantage to using an SCM-managed approach is that tools like GitHub recognize that a change in the skeleton is the _same change_ across all projects that merge with that skeleton. Without the ancestry, with a traditional copy/paste approach, a [commit like this](https://github.com/jaraco/skeleton/commit/12eed1326e1bc26ce256e7b3f8cd8d3a5beab2d5) would produce notifications in the upstream project issue for each and every application, but because it's centralized, GitHub provides just the one notification when the change is added to the skeleton. + +# Usage + +## new projects + +To use skeleton for a new project, simply pull the skeleton into a new project: + +``` +$ git init my-new-project +$ cd my-new-project +$ git pull gh://jaraco/skeleton +``` + +Now customize the project to suit your individual project needs. + +## existing projects + +If you have an existing project, you can still incorporate the skeleton by merging it into the codebase. + +``` +$ git merge skeleton --allow-unrelated-histories +``` + +The `--allow-unrelated-histories` is necessary because the history from the skeleton was previously unrelated to the existing codebase. Resolve any merge conflicts and commit to the master, and now the project is based on the shared skeleton. + +## Updating + +Whenever a change is needed or desired for the general technique for packaging, it can be made in the skeleton project and then merged into each of the derived projects as needed, recommended before each release. As a result, features and best practices for packaging are centrally maintained and readily trickle into a whole suite of packages. This technique lowers the amount of tedious work necessary to create or maintain a project, and coupled with other techniques like continuous integration and deployment, lowers the cost of creating and maintaining refined Python projects to just a few, familiar Git operations. + +For example, here's a session of the [path project](https://pypi.org/project/path) pulling non-conflicting changes from the skeleton: + + + +Thereafter, the target project can make whatever customizations it deems relevant to the scaffolding. The project may even at some point decide that the divergence is too great to merit renewed merging with the original skeleton. This approach applies maximal guidance while creating minimal constraints. + +# Features + +The features/techniques employed by the skeleton include: + +- PEP 517/518-based build relying on Setuptools as the build tool +- Setuptools declarative configuration using setup.cfg +- tox for running tests +- A README.rst as reStructuredText with some popular badges, but with Read the Docs and AppVeyor badges commented out +- A CHANGES.rst file intended for publishing release notes about the project +- Use of [Black](https://black.readthedocs.io/en/stable/) for code formatting (disabled on unsupported Python 3.5 and earlier) +- Integrated type checking through [mypy](https://github.com/python/mypy/). + +## Packaging Conventions + +A pyproject.toml is included to enable PEP 517 and PEP 518 compatibility and declares the requirements necessary to build the project on Setuptools (a minimum version compatible with setup.cfg declarative config). + +The setup.cfg file implements the following features: + +- Assumes universal wheel for release +- Advertises the project's LICENSE file (MIT by default) +- Reads the README.rst file into the long description +- Some common Trove classifiers +- Includes all packages discovered in the repo +- Data files in the package are also included (not just Python files) +- Declares the required Python versions +- Declares install requirements (empty by default) +- Declares setup requirements for legacy environments +- Supplies two 'extras': + - testing: requirements for running tests + - docs: requirements for building docs + - these extras split the declaration into "upstream" (requirements as declared by the skeleton) and "local" (those specific to the local project); these markers help avoid merge conflicts +- Placeholder for defining entry points + +Additionally, the setup.py file declares `use_scm_version` which relies on [setuptools_scm](https://pypi.org/project/setuptools_scm) to do two things: + +- derive the project version from SCM tags +- ensure that all files committed to the repo are automatically included in releases + +## Running Tests + +The skeleton assumes the developer has [tox](https://pypi.org/project/tox) installed. The developer is expected to run `tox` to run tests on the current Python version using [pytest](https://pypi.org/project/pytest). + +Other environments (invoked with `tox -e {name}`) supplied include: + + - a `docs` environment to build the documentation + - a `release` environment to publish the package to PyPI + +A pytest.ini is included to define common options around running tests. In particular: + +- rely on default test discovery in the current directory +- avoid recursing into common directories not containing tests +- run doctests on modules and invoke Flake8 tests +- in doctests, allow Unicode literals and regular literals to match, allowing for doctests to run on Python 2 and 3. Also enable ELLIPSES, a default that would be undone by supplying the prior option. +- filters out known warnings caused by libraries/functionality included by the skeleton + +Relies on a .flake8 file to correct some default behaviors: + +- disable mutually incompatible rules W503 and W504 +- support for Black format + +## Continuous Integration + +The project is pre-configured to run Continuous Integration tests. + +### Github Actions + +[Github Actions](https://docs.github.com/en/free-pro-team@latest/actions) are the preferred provider as they provide free, fast, multi-platform services with straightforward configuration. Configured in `.github/workflows`. + +Features include: +- test against multiple Python versions +- run on late (and updated) platform versions +- automated releases of tagged commits + +### Continuous Deployments + +In addition to running tests, an additional publish stage is configured to automatically release tagged commits to PyPI using [API tokens](https://pypi.org/help/#apitoken). The release process expects an authorized token to be configured with each Github project (or org) `PYPI_TOKEN` [secret](https://docs.github.com/en/free-pro-team@latest/actions/reference/encrypted-secrets). Example: + +``` +pip-run -q jaraco.develop -- -m jaraco.develop.add-github-secrets +``` + +## Building Documentation + +Documentation is automatically built by [Read the Docs](https://readthedocs.org) when the project is registered with it, by way of the .readthedocs.yml file. To test the docs build manually, a tox env may be invoked as `tox -e docs`. Both techniques rely on the dependencies declared in `setup.cfg/options.extras_require.docs`. + +In addition to building the Sphinx docs scaffolded in `docs/`, the docs build a `history.html` file that first injects release dates and hyperlinks into the CHANGES.rst before incorporating it as history in the docs. + +## Cutting releases + +By default, tagged commits are released through the continuous integration deploy stage. + +Releases may also be cut manually by invoking the tox environment `release` with the PyPI token set as the TWINE_PASSWORD: + +``` +TWINE_PASSWORD={token} tox -e release +``` diff --git a/tox.ini b/tox.ini new file mode 100644 index 0000000..7233b94 --- /dev/null +++ b/tox.ini @@ -0,0 +1,40 @@ +[tox] +envlist = python +minversion = 3.2 +# https://github.com/jaraco/skeleton/issues/6 +tox_pip_extensions_ext_venv_update = true +toxworkdir={env:TOX_WORK_DIR:.tox} + + +[testenv] +deps = +commands = + pytest {posargs} +usedevelop = True +extras = testing + +[testenv:docs] +extras = + docs + testing +changedir = docs +commands = + python -m sphinx . {toxinidir}/build/html + +[testenv:release] +skip_install = True +deps = + pep517>=0.5 + twine[keyring]>=1.13 + path + jaraco.develop>=7.1 +passenv = + TWINE_PASSWORD + GITHUB_TOKEN +setenv = + TWINE_USERNAME = {env:TWINE_USERNAME:__token__} +commands = + python -c "import path; path.Path('dist').rmtree_p()" + python -m pep517.build . + python -m twine upload dist/* + python -m jaraco.develop.create-github-release From 2667241f44fed464948cbd140bed1b17cfe4e826 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sat, 12 Dec 2020 23:29:03 -0500 Subject: [PATCH 002/176] Update skeleton description to describe the periodic collapse. Fixes #27. --- skeleton.md | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/skeleton.md b/skeleton.md index ec421c2..dd8ec01 100644 --- a/skeleton.md +++ b/skeleton.md @@ -46,6 +46,26 @@ For example, here's a session of the [path project](https://pypi.org/project/pat Thereafter, the target project can make whatever customizations it deems relevant to the scaffolding. The project may even at some point decide that the divergence is too great to merit renewed merging with the original skeleton. This approach applies maximal guidance while creating minimal constraints. +## Periodic Collapse + +In late 2020, this project [introduced](https://github.com/jaraco/skeleton/issues/27) the idea of a periodic but infrequent (O(years)) collapse of commits to limit the number of commits a new consumer will need to accept to adopt the skeleton. + +The full history of commits is collapsed into a single commit and that commit becomes the new mainline head. + +When one of these collapse operations happens, any project that previously pulled from the skeleton will no longer have a related history with that new main branch. For those projects, the skeleton provides a "handoff" branch that reconciles the two branches. Any project that has previously merged with the skeleton but now gets an error "fatal: refusing to merge unrelated histories" should instead use the handoff branch once to incorporate the new main branch. + +``` +$ git pull https://github.com/jaraco/skeleton 2020-handoff +``` + +This handoff needs to be pulled just once and thereafter the project can pull from the main head. + +The archive and handoff branches from prior collapses are indicate here: + +| refresh | archive | handoff | +|---------|-----------------|--------------| +| 2020-12 | archive/2020-12 | 2020-handoff | + # Features The features/techniques employed by the skeleton include: From 150321caba0dc73489b61d6b5bbfbed52b795ae7 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sun, 13 Dec 2020 14:03:23 -0500 Subject: [PATCH 003/176] Enable automerge --- .github/workflows/automerge.yml | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) create mode 100644 .github/workflows/automerge.yml diff --git a/.github/workflows/automerge.yml b/.github/workflows/automerge.yml new file mode 100644 index 0000000..4f70acf --- /dev/null +++ b/.github/workflows/automerge.yml @@ -0,0 +1,27 @@ +name: automerge +on: + pull_request: + types: + - labeled + - unlabeled + - synchronize + - opened + - edited + - ready_for_review + - reopened + - unlocked + pull_request_review: + types: + - submitted + check_suite: + types: + - completed + status: {} +jobs: + automerge: + runs-on: ubuntu-latest + steps: + - name: automerge + uses: "pascalgn/automerge-action@v0.12.0" + env: + GITHUB_TOKEN: "${{ secrets.GITHUB_TOKEN }}" From 4b1334629e1cb254a1b6853f045f2615b79ec9e1 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Tue, 29 Dec 2020 09:56:52 -0500 Subject: [PATCH 004/176] Automatically inject project name in docs heading. --- docs/index.rst | 4 ++-- setup.cfg | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/index.rst b/docs/index.rst index d14131b..325842b 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -1,5 +1,5 @@ -Welcome to skeleton documentation! -======================================== +Welcome to |project| documentation! +=================================== .. toctree:: :maxdepth: 1 diff --git a/setup.cfg b/setup.cfg index 6321ca7..4fc095b 100644 --- a/setup.cfg +++ b/setup.cfg @@ -37,7 +37,7 @@ testing = docs = # upstream sphinx - jaraco.packaging >= 3.2 + jaraco.packaging >= 8.2 rst.linker >= 1.9 # local From cfe99a5a7941f9f8785dd3ec12d1df94e9134411 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Tue, 29 Dec 2020 21:27:53 -0500 Subject: [PATCH 005/176] pre-commit autoupdate --- .pre-commit-config.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 6639c78..c15ab0c 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,10 +1,10 @@ repos: - repo: https://github.com/psf/black - rev: stable + rev: 20.8b1 hooks: - id: black - repo: https://github.com/asottile/blacken-docs - rev: v1.8.0 + rev: v1.9.1 hooks: - id: blacken-docs From 060d491a9aaacfe457ad365cfd60b611fc9f5bcf Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Wed, 30 Dec 2020 10:57:25 -0500 Subject: [PATCH 006/176] Rename 'Automated Tests' to simply 'tests' --- .github/workflows/main.yml | 2 +- README.rst | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 8c5c232..6a8ff00 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -1,4 +1,4 @@ -name: Automated Tests +name: tests on: [push, pull_request] diff --git a/README.rst b/README.rst index 69554ef..128e61e 100644 --- a/README.rst +++ b/README.rst @@ -6,9 +6,9 @@ .. _PyPI link: https://pypi.org/project/skeleton -.. image:: https://github.com/jaraco/skeleton/workflows/Automated%20Tests/badge.svg - :target: https://github.com/jaraco/skeleton/actions?query=workflow%3A%22Automated+Tests%22 - :alt: Automated Tests +.. image:: https://github.com/jaraco/skeleton/workflows/tests/badge.svg + :target: https://github.com/jaraco/skeleton/actions?query=workflow%3A%22tests%22 + :alt: tests .. image:: https://img.shields.io/badge/code%20style-black-000000.svg :target: https://github.com/psf/black From 2b839bad1c2189f4eeb0f74c4a2455ba6687741b Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Wed, 30 Dec 2020 12:06:13 -0500 Subject: [PATCH 007/176] Add note about automatic merging of PRs and the requirements and limitations. --- skeleton.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/skeleton.md b/skeleton.md index dd8ec01..0938f89 100644 --- a/skeleton.md +++ b/skeleton.md @@ -138,6 +138,8 @@ Features include: - test against multiple Python versions - run on late (and updated) platform versions - automated releases of tagged commits +- [automatic merging of PRs](https://github.com/marketplace/actions/merge-pull-requests) (requires [protecting branches with required status checks](https://docs.github.com/en/free-pro-team@latest/github/administering-a-repository/enabling-required-status-checks), [not possible through API](https://github.community/t/set-all-status-checks-to-be-required-as-branch-protection-using-the-github-api/119493)) + ### Continuous Deployments From a36768aa363c8f7b54aae00e11f895ff06337532 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Wed, 30 Dec 2020 22:20:46 -0500 Subject: [PATCH 008/176] Prefer pytest-enabler to jaraco.test --- pyproject.toml | 10 ++++------ setup.cfg | 3 +-- 2 files changed, 5 insertions(+), 8 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 79f088a..b6ebc0b 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -7,16 +7,14 @@ skip-string-normalization = true [tool.setuptools_scm] -# jaraco/skeleton#22 -[tool.jaraco.pytest.plugins.black] +[pytest.enabler.black] addopts = "--black" -# jaraco/skeleton#22 -[tool.jaraco.pytest.plugins.mypy] +[pytest.enabler.mypy] addopts = "--mypy" -[tool.jaraco.pytest.plugins.flake8] +[pytest.enabler.flake8] addopts = "--flake8" -[tool.jaraco.pytest.plugins.cov] +[pytest.enabler.cov] addopts = "--cov" diff --git a/setup.cfg b/setup.cfg index 4fc095b..d5010f7 100644 --- a/setup.cfg +++ b/setup.cfg @@ -29,8 +29,7 @@ testing = pytest-black >= 0.3.7; python_implementation != "PyPy" pytest-cov pytest-mypy; python_implementation != "PyPy" - # jaraco/skeleton#22 - jaraco.test >= 3.2.0 + pytest-enabler # local From 3e876d7906fa6387ab6ac9a9bff8659762363017 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Fri, 8 Jan 2021 23:14:07 -0500 Subject: [PATCH 009/176] Enable complexity limit. Fixes jaraco/skeleton#34. --- .flake8 | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.flake8 b/.flake8 index 790c109..59a51f8 100644 --- a/.flake8 +++ b/.flake8 @@ -1,5 +1,9 @@ [flake8] max-line-length = 88 + +# jaraco/skeleton#34 +max-complexity = 10 + ignore = # W503 violates spec https://github.com/PyCQA/pycodestyle/issues/513 W503 From 1731fbebe9f6655a203e6e08ab309f9916ea6f65 Mon Sep 17 00:00:00 2001 From: Sviatoslav Sydorenko Date: Sat, 9 Jan 2021 05:21:12 +0100 Subject: [PATCH 010/176] Replace pep517.build with build (#37) * Replace pep517.build with build Resolves #30 * Prefer simple usage Co-authored-by: Jason R. Coombs --- tox.ini | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tox.ini b/tox.ini index 7233b94..249f97c 100644 --- a/tox.ini +++ b/tox.ini @@ -24,7 +24,7 @@ commands = [testenv:release] skip_install = True deps = - pep517>=0.5 + build twine[keyring]>=1.13 path jaraco.develop>=7.1 @@ -35,6 +35,6 @@ setenv = TWINE_USERNAME = {env:TWINE_USERNAME:__token__} commands = python -c "import path; path.Path('dist').rmtree_p()" - python -m pep517.build . + python -m build python -m twine upload dist/* python -m jaraco.develop.create-github-release From a9b3f681dea9728235c2a9c68165f7b5cbf350ab Mon Sep 17 00:00:00 2001 From: Sviatoslav Sydorenko Date: Sat, 9 Jan 2021 05:27:24 +0100 Subject: [PATCH 011/176] Use license_files instead of license_file in meta (#35) Singular `license_file` is deprecated since wheel v0.32.0. Refs: * https://wheel.readthedocs.io/en/stable/news.html * https://wheel.readthedocs.io/en/stable/user_guide.html#including-license-files-in-the-generated-wheel-file --- setup.cfg | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/setup.cfg b/setup.cfg index d5010f7..88bc263 100644 --- a/setup.cfg +++ b/setup.cfg @@ -1,5 +1,6 @@ [metadata] -license_file = LICENSE +license_files = + LICENSE name = skeleton author = Jason R. Coombs author_email = jaraco@jaraco.com From 77fbe1df4af6d8f75f44440e89ee1bc249c9f2e0 Mon Sep 17 00:00:00 2001 From: Sviatoslav Sydorenko Date: Sat, 9 Jan 2021 05:37:11 +0100 Subject: [PATCH 012/176] Use `extend-ignore` in flake8 config (#33) * Use `extend-ignore` in flake8 config This option allows to add extra ignored rules to the default list instead of replacing it. The default exclusions are: E121, E123, E126, E226, E24, E704, W503 and W504. Fixes #28. Refs: * https://github.com/pypa/setuptools/pull/2486/files#r541943356 * https://flake8.pycqa.org/en/latest/user/options.html#cmdoption-flake8-extend-ignore * https://flake8.pycqa.org/en/latest/user/options.html#cmdoption-flake8-ignore * Enable complexity limit. Fixes jaraco/skeleton#34. * Replace pep517.build with build (#37) * Replace pep517.build with build Resolves #30 * Prefer simple usage Co-authored-by: Jason R. Coombs * Use license_files instead of license_file in meta (#35) Singular `license_file` is deprecated since wheel v0.32.0. Refs: * https://wheel.readthedocs.io/en/stable/news.html * https://wheel.readthedocs.io/en/stable/user_guide.html#including-license-files-in-the-generated-wheel-file Co-authored-by: Jason R. Coombs --- .flake8 | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/.flake8 b/.flake8 index 59a51f8..48b2e24 100644 --- a/.flake8 +++ b/.flake8 @@ -4,10 +4,6 @@ max-line-length = 88 # jaraco/skeleton#34 max-complexity = 10 -ignore = - # W503 violates spec https://github.com/PyCQA/pycodestyle/issues/513 - W503 - # W504 has issues https://github.com/OCA/maintainer-quality-tools/issues/545 - W504 +extend-ignore = # Black creates whitespace before colon E203 From 0df40810ec54590c888ae0e4073d73f731c91f4a Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Fri, 15 Jan 2021 19:16:28 -0500 Subject: [PATCH 013/176] Add support for namespace packages. Closes jaraco/skeleton#40. --- setup.cfg | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/setup.cfg b/setup.cfg index 88bc263..106763e 100644 --- a/setup.cfg +++ b/setup.cfg @@ -15,12 +15,18 @@ classifiers = Programming Language :: Python :: 3 :: Only [options] -packages = find: +packages = find_namespace: include_package_data = true python_requires = >=3.6 install_requires = setup_requires = setuptools_scm[toml] >= 3.4.1 +[options.packages.find] +exclude = + build* + docs* + tests* + [options.extras_require] testing = # upstream From 51298a2cc4faa7253e9fe41d7a9574cf9aac997c Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Tue, 9 Feb 2021 23:08:58 -0500 Subject: [PATCH 014/176] Normalize indentation --- setup.cfg | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/setup.cfg b/setup.cfg index 106763e..8df8d27 100644 --- a/setup.cfg +++ b/setup.cfg @@ -23,9 +23,9 @@ setup_requires = setuptools_scm[toml] >= 3.4.1 [options.packages.find] exclude = - build* - docs* - tests* + build* + docs* + tests* [options.extras_require] testing = From 743af7249d56e55a7c2c5f3111958ceee008d8ea Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sun, 21 Feb 2021 13:04:46 -0500 Subject: [PATCH 015/176] Exclude dist from discovered packages. Fixes jaraco/skeleton#46. --- setup.cfg | 1 + 1 file changed, 1 insertion(+) diff --git a/setup.cfg b/setup.cfg index 8df8d27..af24641 100644 --- a/setup.cfg +++ b/setup.cfg @@ -24,6 +24,7 @@ setup_requires = setuptools_scm[toml] >= 3.4.1 [options.packages.find] exclude = build* + dist* docs* tests* From 38fff62edb5e282f144dc77cc1bf5555367336d9 Mon Sep 17 00:00:00 2001 From: KOLANICH Date: Sat, 6 Feb 2021 23:03:13 +0300 Subject: [PATCH 016/176] Added an .editorconfig. Pull request jaraco/skeleton#43. --- .editorconfig | 15 +++++++++++++++ pytest.ini | 4 ++-- 2 files changed, 17 insertions(+), 2 deletions(-) create mode 100644 .editorconfig diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..6385b57 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,15 @@ +root = true + +[*] +charset = utf-8 +indent_style = tab +indent_size = 4 +insert_final_newline = true +end_of_line = lf + +[*.py] +indent_style = space + +[*.{yml,yaml}] +indent_style = space +indent_size = 2 diff --git a/pytest.ini b/pytest.ini index d7f0b11..016063b 100644 --- a/pytest.ini +++ b/pytest.ini @@ -5,5 +5,5 @@ doctest_optionflags=ALLOW_UNICODE ELLIPSIS # workaround for warning pytest-dev/pytest#6178 junit_family=xunit2 filterwarnings= - # https://github.com/pytest-dev/pytest/issues/6928 - ignore:direct construction of .*Item has been deprecated:DeprecationWarning + # https://github.com/pytest-dev/pytest/issues/6928 + ignore:direct construction of .*Item has been deprecated:DeprecationWarning From 5e416793c008c5ef285c37828072fbea5ced6d08 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sun, 21 Feb 2021 21:34:35 -0500 Subject: [PATCH 017/176] It's no longer necessary to filter this warning and it's not a warning anymore. --- pytest.ini | 2 -- 1 file changed, 2 deletions(-) diff --git a/pytest.ini b/pytest.ini index 016063b..6bf69af 100644 --- a/pytest.ini +++ b/pytest.ini @@ -5,5 +5,3 @@ doctest_optionflags=ALLOW_UNICODE ELLIPSIS # workaround for warning pytest-dev/pytest#6178 junit_family=xunit2 filterwarnings= - # https://github.com/pytest-dev/pytest/issues/6928 - ignore:direct construction of .*Item has been deprecated:DeprecationWarning From d9a13c77ce2a3efea70c97d219ca4335c0f03c40 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sun, 21 Feb 2021 21:36:53 -0500 Subject: [PATCH 018/176] Bump minimum pytest --- setup.cfg | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.cfg b/setup.cfg index af24641..81f70ee 100644 --- a/setup.cfg +++ b/setup.cfg @@ -31,7 +31,7 @@ exclude = [options.extras_require] testing = # upstream - pytest >= 3.5, !=3.7.3 + pytest >= 4.6 pytest-checkdocs >= 1.2.3 pytest-flake8 pytest-black >= 0.3.7; python_implementation != "PyPy" From bf9fae2c0df316dc837d56ae68880620733d5ff6 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sat, 6 Mar 2021 09:57:43 -0500 Subject: [PATCH 019/176] Require twine 3 with keyring unconditionally required. --- tox.ini | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tox.ini b/tox.ini index 249f97c..a9a50b0 100644 --- a/tox.ini +++ b/tox.ini @@ -25,7 +25,7 @@ commands = skip_install = True deps = build - twine[keyring]>=1.13 + twine>=3 path jaraco.develop>=7.1 passenv = From 7bdab57872da46ef6a5a7f5ea9099a197bdc3131 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sun, 7 Mar 2021 12:23:48 -0500 Subject: [PATCH 020/176] Add comments indicating why the exclusions are present --- setup.cfg | 2 ++ 1 file changed, 2 insertions(+) diff --git a/setup.cfg b/setup.cfg index 81f70ee..dd215c6 100644 --- a/setup.cfg +++ b/setup.cfg @@ -34,8 +34,10 @@ testing = pytest >= 4.6 pytest-checkdocs >= 1.2.3 pytest-flake8 + # python_implementation: workaround for jaraco/skeleton#22 pytest-black >= 0.3.7; python_implementation != "PyPy" pytest-cov + # python_implementation: workaround for jaraco/skeleton#22 pytest-mypy; python_implementation != "PyPy" pytest-enabler From 14312a5bd75d3313ffd3e14fc7fbbc2a9b05cee5 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sun, 7 Mar 2021 12:24:21 -0500 Subject: [PATCH 021/176] Exclude mypy on Python 3.10 as workaround for python/typed_ast#156. --- setup.cfg | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/setup.cfg b/setup.cfg index dd215c6..55497f8 100644 --- a/setup.cfg +++ b/setup.cfg @@ -38,7 +38,8 @@ testing = pytest-black >= 0.3.7; python_implementation != "PyPy" pytest-cov # python_implementation: workaround for jaraco/skeleton#22 - pytest-mypy; python_implementation != "PyPy" + # python_version: workaround for python/typed_ast#156 + pytest-mypy; python_implementation != "PyPy" and python_version < "3.10" pytest-enabler # local From af5445115af0cb68e671a678538a0207389586be Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sun, 7 Mar 2021 12:30:25 -0500 Subject: [PATCH 022/176] Bump minimums on pytest-checkdocs and pytest-enabler as found on Setuptools. --- setup.cfg | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/setup.cfg b/setup.cfg index 55497f8..3f6610b 100644 --- a/setup.cfg +++ b/setup.cfg @@ -32,7 +32,7 @@ exclude = testing = # upstream pytest >= 4.6 - pytest-checkdocs >= 1.2.3 + pytest-checkdocs >= 2.4 pytest-flake8 # python_implementation: workaround for jaraco/skeleton#22 pytest-black >= 0.3.7; python_implementation != "PyPy" @@ -40,7 +40,7 @@ testing = # python_implementation: workaround for jaraco/skeleton#22 # python_version: workaround for python/typed_ast#156 pytest-mypy; python_implementation != "PyPy" and python_version < "3.10" - pytest-enabler + pytest-enabler >= 1.0.1 # local From 86efb884f805a9e1f64661ec758f3bd084fed515 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sun, 7 Mar 2021 12:53:54 -0500 Subject: [PATCH 023/176] Also deny black on Python 3.10 as workaround for python/typed_ast#156. --- setup.cfg | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/setup.cfg b/setup.cfg index 3f6610b..52876d5 100644 --- a/setup.cfg +++ b/setup.cfg @@ -35,7 +35,8 @@ testing = pytest-checkdocs >= 2.4 pytest-flake8 # python_implementation: workaround for jaraco/skeleton#22 - pytest-black >= 0.3.7; python_implementation != "PyPy" + # python_version: workaround for python/typed_ast#156 + pytest-black >= 0.3.7; python_implementation != "PyPy" and python_version < "3.10" pytest-cov # python_implementation: workaround for jaraco/skeleton#22 # python_version: workaround for python/typed_ast#156 From 7fe4ab8294a843622d20face7f9f6ccddb2d0a14 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Mon, 15 Mar 2021 18:31:04 -0400 Subject: [PATCH 024/176] Add leading */ to coverage.run.omit. Workaround for pytest-dev/pytest-cov#456. --- .coveragerc | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.coveragerc b/.coveragerc index 4582306..6a34e66 100644 --- a/.coveragerc +++ b/.coveragerc @@ -1,5 +1,7 @@ [run] -omit = .tox/* +omit = + # leading `*/` for pytest-dev/pytest-cov#456 + */.tox/* [report] show_missing = True From 6e2d0ba00b60c10466b0e040e2d4b1206c3f0b3d Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sun, 25 Apr 2021 20:38:58 -0400 Subject: [PATCH 025/176] Remove automerge. Fixes jaraco/skeleton#49. --- .github/workflows/automerge.yml | 27 --------------------------- 1 file changed, 27 deletions(-) delete mode 100644 .github/workflows/automerge.yml diff --git a/.github/workflows/automerge.yml b/.github/workflows/automerge.yml deleted file mode 100644 index 4f70acf..0000000 --- a/.github/workflows/automerge.yml +++ /dev/null @@ -1,27 +0,0 @@ -name: automerge -on: - pull_request: - types: - - labeled - - unlabeled - - synchronize - - opened - - edited - - ready_for_review - - reopened - - unlocked - pull_request_review: - types: - - submitted - check_suite: - types: - - completed - status: {} -jobs: - automerge: - runs-on: ubuntu-latest - steps: - - name: automerge - uses: "pascalgn/automerge-action@v0.12.0" - env: - GITHUB_TOKEN: "${{ secrets.GITHUB_TOKEN }}" From 2f690f6083feea9a16ea3711f391d598a2ed1228 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sun, 25 Apr 2021 21:19:48 -0400 Subject: [PATCH 026/176] Enable dependabot (#50) * Added a config for dependabot. * Update features list for dependabot. Co-authored-by: KOLANICH --- .github/dependabot.yml | 8 ++++++++ skeleton.md | 1 + 2 files changed, 9 insertions(+) create mode 100644 .github/dependabot.yml diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 0000000..89ff339 --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,8 @@ +version: 2 +updates: + - package-ecosystem: "pip" + directory: "/" + schedule: + interval: "daily" + allow: + - dependency-type: "all" diff --git a/skeleton.md b/skeleton.md index 0938f89..af5f2ca 100644 --- a/skeleton.md +++ b/skeleton.md @@ -77,6 +77,7 @@ The features/techniques employed by the skeleton include: - A CHANGES.rst file intended for publishing release notes about the project - Use of [Black](https://black.readthedocs.io/en/stable/) for code formatting (disabled on unsupported Python 3.5 and earlier) - Integrated type checking through [mypy](https://github.com/python/mypy/). +- Dependabot enabled to enable supply chain security. ## Packaging Conventions From 6c1c45bc1ce8ab01d91324a46c584172664a0104 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sun, 25 Apr 2021 21:52:29 -0400 Subject: [PATCH 027/176] Replace md file with badge linking to documentation site. Fixes jaraco/skeleton#47. --- README.rst | 3 + skeleton.md | 167 ---------------------------------------------------- 2 files changed, 3 insertions(+), 167 deletions(-) delete mode 100644 skeleton.md diff --git a/README.rst b/README.rst index 128e61e..a3e1b74 100644 --- a/README.rst +++ b/README.rst @@ -16,3 +16,6 @@ .. .. image:: https://readthedocs.org/projects/skeleton/badge/?version=latest .. :target: https://skeleton.readthedocs.io/en/latest/?badge=latest + +.. image:: https://img.shields.io/badge/skeleton-2021-informational + :target: https://blog.jaraco.com/skeleton diff --git a/skeleton.md b/skeleton.md deleted file mode 100644 index af5f2ca..0000000 --- a/skeleton.md +++ /dev/null @@ -1,167 +0,0 @@ -# Overview - -This project is merged with [skeleton](https://github.com/jaraco/skeleton). What is skeleton? It's the scaffolding of a Python project jaraco [introduced in his blog](https://blog.jaraco.com/a-project-skeleton-for-python-projects/). It seeks to provide a means to re-use techniques and inherit advances when managing projects for distribution. - -## An SCM-Managed Approach - -While maintaining dozens of projects in PyPI, jaraco derives best practices for project distribution and publishes them in the [skeleton repo](https://github.com/jaraco/skeleton), a Git repo capturing the evolution and culmination of these best practices. - -It's intended to be used by a new or existing project to adopt these practices and honed and proven techniques. Adopters are encouraged to use the project directly and maintain a small deviation from the technique, make their own fork for more substantial changes unique to their environment or preferences, or simply adopt the skeleton once and abandon it thereafter. - -The primary advantage to using an SCM for maintaining these techniques is that those tools help facilitate the merge between the template and its adopting projects. - -Another advantage to using an SCM-managed approach is that tools like GitHub recognize that a change in the skeleton is the _same change_ across all projects that merge with that skeleton. Without the ancestry, with a traditional copy/paste approach, a [commit like this](https://github.com/jaraco/skeleton/commit/12eed1326e1bc26ce256e7b3f8cd8d3a5beab2d5) would produce notifications in the upstream project issue for each and every application, but because it's centralized, GitHub provides just the one notification when the change is added to the skeleton. - -# Usage - -## new projects - -To use skeleton for a new project, simply pull the skeleton into a new project: - -``` -$ git init my-new-project -$ cd my-new-project -$ git pull gh://jaraco/skeleton -``` - -Now customize the project to suit your individual project needs. - -## existing projects - -If you have an existing project, you can still incorporate the skeleton by merging it into the codebase. - -``` -$ git merge skeleton --allow-unrelated-histories -``` - -The `--allow-unrelated-histories` is necessary because the history from the skeleton was previously unrelated to the existing codebase. Resolve any merge conflicts and commit to the master, and now the project is based on the shared skeleton. - -## Updating - -Whenever a change is needed or desired for the general technique for packaging, it can be made in the skeleton project and then merged into each of the derived projects as needed, recommended before each release. As a result, features and best practices for packaging are centrally maintained and readily trickle into a whole suite of packages. This technique lowers the amount of tedious work necessary to create or maintain a project, and coupled with other techniques like continuous integration and deployment, lowers the cost of creating and maintaining refined Python projects to just a few, familiar Git operations. - -For example, here's a session of the [path project](https://pypi.org/project/path) pulling non-conflicting changes from the skeleton: - - - -Thereafter, the target project can make whatever customizations it deems relevant to the scaffolding. The project may even at some point decide that the divergence is too great to merit renewed merging with the original skeleton. This approach applies maximal guidance while creating minimal constraints. - -## Periodic Collapse - -In late 2020, this project [introduced](https://github.com/jaraco/skeleton/issues/27) the idea of a periodic but infrequent (O(years)) collapse of commits to limit the number of commits a new consumer will need to accept to adopt the skeleton. - -The full history of commits is collapsed into a single commit and that commit becomes the new mainline head. - -When one of these collapse operations happens, any project that previously pulled from the skeleton will no longer have a related history with that new main branch. For those projects, the skeleton provides a "handoff" branch that reconciles the two branches. Any project that has previously merged with the skeleton but now gets an error "fatal: refusing to merge unrelated histories" should instead use the handoff branch once to incorporate the new main branch. - -``` -$ git pull https://github.com/jaraco/skeleton 2020-handoff -``` - -This handoff needs to be pulled just once and thereafter the project can pull from the main head. - -The archive and handoff branches from prior collapses are indicate here: - -| refresh | archive | handoff | -|---------|-----------------|--------------| -| 2020-12 | archive/2020-12 | 2020-handoff | - -# Features - -The features/techniques employed by the skeleton include: - -- PEP 517/518-based build relying on Setuptools as the build tool -- Setuptools declarative configuration using setup.cfg -- tox for running tests -- A README.rst as reStructuredText with some popular badges, but with Read the Docs and AppVeyor badges commented out -- A CHANGES.rst file intended for publishing release notes about the project -- Use of [Black](https://black.readthedocs.io/en/stable/) for code formatting (disabled on unsupported Python 3.5 and earlier) -- Integrated type checking through [mypy](https://github.com/python/mypy/). -- Dependabot enabled to enable supply chain security. - -## Packaging Conventions - -A pyproject.toml is included to enable PEP 517 and PEP 518 compatibility and declares the requirements necessary to build the project on Setuptools (a minimum version compatible with setup.cfg declarative config). - -The setup.cfg file implements the following features: - -- Assumes universal wheel for release -- Advertises the project's LICENSE file (MIT by default) -- Reads the README.rst file into the long description -- Some common Trove classifiers -- Includes all packages discovered in the repo -- Data files in the package are also included (not just Python files) -- Declares the required Python versions -- Declares install requirements (empty by default) -- Declares setup requirements for legacy environments -- Supplies two 'extras': - - testing: requirements for running tests - - docs: requirements for building docs - - these extras split the declaration into "upstream" (requirements as declared by the skeleton) and "local" (those specific to the local project); these markers help avoid merge conflicts -- Placeholder for defining entry points - -Additionally, the setup.py file declares `use_scm_version` which relies on [setuptools_scm](https://pypi.org/project/setuptools_scm) to do two things: - -- derive the project version from SCM tags -- ensure that all files committed to the repo are automatically included in releases - -## Running Tests - -The skeleton assumes the developer has [tox](https://pypi.org/project/tox) installed. The developer is expected to run `tox` to run tests on the current Python version using [pytest](https://pypi.org/project/pytest). - -Other environments (invoked with `tox -e {name}`) supplied include: - - - a `docs` environment to build the documentation - - a `release` environment to publish the package to PyPI - -A pytest.ini is included to define common options around running tests. In particular: - -- rely on default test discovery in the current directory -- avoid recursing into common directories not containing tests -- run doctests on modules and invoke Flake8 tests -- in doctests, allow Unicode literals and regular literals to match, allowing for doctests to run on Python 2 and 3. Also enable ELLIPSES, a default that would be undone by supplying the prior option. -- filters out known warnings caused by libraries/functionality included by the skeleton - -Relies on a .flake8 file to correct some default behaviors: - -- disable mutually incompatible rules W503 and W504 -- support for Black format - -## Continuous Integration - -The project is pre-configured to run Continuous Integration tests. - -### Github Actions - -[Github Actions](https://docs.github.com/en/free-pro-team@latest/actions) are the preferred provider as they provide free, fast, multi-platform services with straightforward configuration. Configured in `.github/workflows`. - -Features include: -- test against multiple Python versions -- run on late (and updated) platform versions -- automated releases of tagged commits -- [automatic merging of PRs](https://github.com/marketplace/actions/merge-pull-requests) (requires [protecting branches with required status checks](https://docs.github.com/en/free-pro-team@latest/github/administering-a-repository/enabling-required-status-checks), [not possible through API](https://github.community/t/set-all-status-checks-to-be-required-as-branch-protection-using-the-github-api/119493)) - - -### Continuous Deployments - -In addition to running tests, an additional publish stage is configured to automatically release tagged commits to PyPI using [API tokens](https://pypi.org/help/#apitoken). The release process expects an authorized token to be configured with each Github project (or org) `PYPI_TOKEN` [secret](https://docs.github.com/en/free-pro-team@latest/actions/reference/encrypted-secrets). Example: - -``` -pip-run -q jaraco.develop -- -m jaraco.develop.add-github-secrets -``` - -## Building Documentation - -Documentation is automatically built by [Read the Docs](https://readthedocs.org) when the project is registered with it, by way of the .readthedocs.yml file. To test the docs build manually, a tox env may be invoked as `tox -e docs`. Both techniques rely on the dependencies declared in `setup.cfg/options.extras_require.docs`. - -In addition to building the Sphinx docs scaffolded in `docs/`, the docs build a `history.html` file that first injects release dates and hyperlinks into the CHANGES.rst before incorporating it as history in the docs. - -## Cutting releases - -By default, tagged commits are released through the continuous integration deploy stage. - -Releases may also be cut manually by invoking the tox environment `release` with the PyPI token set as the TWINE_PASSWORD: - -``` -TWINE_PASSWORD={token} tox -e release -``` From 8698127dbd17b47d1d07e35bee3725fecb69670b Mon Sep 17 00:00:00 2001 From: Sviatoslav Sydorenko Date: Mon, 26 Apr 2021 04:10:20 +0200 Subject: [PATCH 028/176] Make sphinx fail on any warnings (#36) This change adds `nitpicky=True` (which is an equivalent of `-n`) to make Sphinx emit warnings for any references to non-existing targets. Then, it adds `-W` to make it fail whenever a single warning is seen. Finally, `--keep-going` allows Sphinx to print out all the warnings before exiting instead of showing just one and bailing. Resolves #29 Refs: * https://www.sphinx-doc.org/en/master/man/sphinx-build.html#cmdoption-sphinx-build-n * https://www.sphinx-doc.org/en/master/man/sphinx-build.html#cmdoption-sphinx-build-W * https://www.sphinx-doc.org/en/master/man/sphinx-build.html#cmdoption-sphinx-build-keep-going --- docs/conf.py | 3 +++ tox.ini | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/docs/conf.py b/docs/conf.py index 433d185..f65d1fa 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -24,3 +24,6 @@ ], ) } + +# Be strict about any broken references: +nitpicky = True diff --git a/tox.ini b/tox.ini index a9a50b0..6984890 100644 --- a/tox.ini +++ b/tox.ini @@ -19,7 +19,7 @@ extras = testing changedir = docs commands = - python -m sphinx . {toxinidir}/build/html + python -m sphinx -W --keep-going . {toxinidir}/build/html [testenv:release] skip_install = True From 4a734d4841b0ad5fddad3c2524e512f608c82d74 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Thu, 20 May 2021 14:01:53 -0400 Subject: [PATCH 029/176] Test on Python 3.10 --- .github/workflows/main.yml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 6a8ff00..7d6b455 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -6,7 +6,10 @@ jobs: test: strategy: matrix: - python: [3.6, 3.8, 3.9] + python: + - 3.6 + - 3.9 + - 3.10.0-alpha - 3.10.99 platform: [ubuntu-latest, macos-latest, windows-latest] runs-on: ${{ matrix.platform }} steps: From 1b165200642e74a4c2acebf7fedb28e732a17881 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Wed, 26 May 2021 10:40:46 -0400 Subject: [PATCH 030/176] Remove setup_requires, obviated by build-requires in pyproject.toml. --- setup.cfg | 1 - 1 file changed, 1 deletion(-) diff --git a/setup.cfg b/setup.cfg index 52876d5..e768c6b 100644 --- a/setup.cfg +++ b/setup.cfg @@ -19,7 +19,6 @@ packages = find_namespace: include_package_data = true python_requires = >=3.6 install_requires = -setup_requires = setuptools_scm[toml] >= 3.4.1 [options.packages.find] exclude = From 85d08db3ef3811bd208995254e7e9c9658cf710d Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Thu, 3 Jun 2021 19:55:51 -0400 Subject: [PATCH 031/176] Suppress deprecation warnings in flake8 and packaging.tags. Ref pypa/packaging#433. --- pytest.ini | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/pytest.ini b/pytest.ini index 6bf69af..31b114f 100644 --- a/pytest.ini +++ b/pytest.ini @@ -5,3 +5,7 @@ doctest_optionflags=ALLOW_UNICODE ELLIPSIS # workaround for warning pytest-dev/pytest#6178 junit_family=xunit2 filterwarnings= + # Suppress deprecation warning in flake8 + ignore:SelectableGroups dict interface is deprecated::flake8 + # Suppress deprecation warning in pypa/packaging#433 + ignore:The distutils package is deprecated::packaging.tags From 5a8384e53c59a886f982739c02572732afa76c7f Mon Sep 17 00:00:00 2001 From: Brian Rutledge Date: Sat, 12 Jun 2021 10:10:07 -0400 Subject: [PATCH 032/176] Use shutil for rmtree --- tox.ini | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/tox.ini b/tox.ini index 6984890..3ca2af3 100644 --- a/tox.ini +++ b/tox.ini @@ -26,7 +26,6 @@ skip_install = True deps = build twine>=3 - path jaraco.develop>=7.1 passenv = TWINE_PASSWORD @@ -34,7 +33,7 @@ passenv = setenv = TWINE_USERNAME = {env:TWINE_USERNAME:__token__} commands = - python -c "import path; path.Path('dist').rmtree_p()" + python -c "import shutil; shutil.rmtree('dist', ignore_errors=True)" python -m build python -m twine upload dist/* python -m jaraco.develop.create-github-release From 14787e69e793d68c8ac17f010dc45891ee0a492c Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Fri, 16 Jul 2021 09:03:21 -0400 Subject: [PATCH 033/176] Rely on setuptools 56 and drop the explicit mention of the license file in favor of simple discovery. --- pyproject.toml | 2 +- setup.cfg | 2 -- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index b6ebc0b..28bd788 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,5 +1,5 @@ [build-system] -requires = ["setuptools>=42", "wheel", "setuptools_scm[toml]>=3.4.1"] +requires = ["setuptools>=56", "wheel", "setuptools_scm[toml]>=3.4.1"] build-backend = "setuptools.build_meta" [tool.black] diff --git a/setup.cfg b/setup.cfg index e768c6b..53387b6 100644 --- a/setup.cfg +++ b/setup.cfg @@ -1,6 +1,4 @@ [metadata] -license_files = - LICENSE name = skeleton author = Jason R. Coombs author_email = jaraco@jaraco.com From 212e995cd366010a8c372ea2fedfbb8be471e5cb Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Fri, 16 Jul 2021 09:07:59 -0400 Subject: [PATCH 034/176] Remove workaround for python/typed_ast#156. --- setup.cfg | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/setup.cfg b/setup.cfg index 53387b6..80fd268 100644 --- a/setup.cfg +++ b/setup.cfg @@ -32,12 +32,10 @@ testing = pytest-checkdocs >= 2.4 pytest-flake8 # python_implementation: workaround for jaraco/skeleton#22 - # python_version: workaround for python/typed_ast#156 - pytest-black >= 0.3.7; python_implementation != "PyPy" and python_version < "3.10" + pytest-black >= 0.3.7; python_implementation != "PyPy" pytest-cov # python_implementation: workaround for jaraco/skeleton#22 - # python_version: workaround for python/typed_ast#156 - pytest-mypy; python_implementation != "PyPy" and python_version < "3.10" + pytest-mypy; python_implementation != "PyPy" pytest-enabler >= 1.0.1 # local From 498b965a805224420c8cde5969bf342a41766227 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Fri, 16 Jul 2021 09:19:05 -0400 Subject: [PATCH 035/176] Use line continuations to indicate which exclusions are for which workarounds. --- setup.cfg | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/setup.cfg b/setup.cfg index 80fd268..69eb0ee 100644 --- a/setup.cfg +++ b/setup.cfg @@ -31,11 +31,13 @@ testing = pytest >= 4.6 pytest-checkdocs >= 2.4 pytest-flake8 - # python_implementation: workaround for jaraco/skeleton#22 - pytest-black >= 0.3.7; python_implementation != "PyPy" + pytest-black >= 0.3.7; \ + # workaround for jaraco/skeleton#22 + python_implementation != "PyPy" pytest-cov - # python_implementation: workaround for jaraco/skeleton#22 - pytest-mypy; python_implementation != "PyPy" + pytest-mypy; \ + # workaround for jaraco/skeleton#22 + python_implementation != "PyPy" pytest-enabler >= 1.0.1 # local From 719a7ced8a1713b7fe94d842a8f6fec7425b8a0a Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Wed, 21 Jul 2021 17:33:29 -0400 Subject: [PATCH 036/176] Remove blacken docs as it cannot honor Python's default repr. Ref asottile/blacken-docs#62. --- .pre-commit-config.yaml | 5 ----- 1 file changed, 5 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index c15ab0c..f66bf56 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -3,8 +3,3 @@ repos: rev: 20.8b1 hooks: - id: black - -- repo: https://github.com/asottile/blacken-docs - rev: v1.9.1 - hooks: - - id: blacken-docs From a76a548d0f25947d2594d36b784d029a6ada977f Mon Sep 17 00:00:00 2001 From: Alan Fregtman <941331+darkvertex@users.noreply.github.com> Date: Mon, 26 Jul 2021 10:55:08 -0400 Subject: [PATCH 037/176] .editorconfig: Set max_line_length to 88 for Python files. --- .editorconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/.editorconfig b/.editorconfig index 6385b57..b8aeea1 100644 --- a/.editorconfig +++ b/.editorconfig @@ -9,6 +9,7 @@ end_of_line = lf [*.py] indent_style = space +max_line_length = 88 [*.{yml,yaml}] indent_style = space From 8ea55f2fb26bd77997f0e9435bab2d41376a76d4 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Fri, 17 Sep 2021 21:23:38 -0400 Subject: [PATCH 038/176] Add intersphinx mappings for Python to prevent spurious nitpicky failures. Fixes jaraco/skeleton#51. --- docs/conf.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/docs/conf.py b/docs/conf.py index f65d1fa..4ae7409 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -27,3 +27,10 @@ # Be strict about any broken references: nitpicky = True + +# Include Python intersphinx mapping to prevent failures +# jaraco/skeleton#51 +extensions += ['sphinx.ext.intersphinx'] +intersphinx_mapping = { + 'python': ('https://docs.python.org/3', None), +} From dc43378c8accd85321b42e3fe69fcb87e5266006 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Tue, 5 Oct 2021 22:45:21 -0400 Subject: [PATCH 039/176] Test on Python 3.10 (final). --- .github/workflows/main.yml | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 7d6b455..6aad7f1 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -9,8 +9,11 @@ jobs: python: - 3.6 - 3.9 - - 3.10.0-alpha - 3.10.99 - platform: [ubuntu-latest, macos-latest, windows-latest] + - "3.10" + platform: + - ubuntu-latest + - macos-latest + - windows-latest runs-on: ${{ matrix.platform }} steps: - uses: actions/checkout@v2 @@ -34,7 +37,7 @@ jobs: - name: Setup Python uses: actions/setup-python@v2 with: - python-version: 3.9 + python-version: "3.10" - name: Install tox run: | python -m pip install tox From 5823e9ca9d242b733a5ff3c8e2c22e13ec0a4c01 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Thu, 7 Oct 2021 19:52:04 -0400 Subject: [PATCH 040/176] Rely on pytest 6 and drop workaround for pytest-dev/pytest#6178. --- pytest.ini | 2 -- setup.cfg | 2 +- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/pytest.ini b/pytest.ini index 31b114f..9ecdba4 100644 --- a/pytest.ini +++ b/pytest.ini @@ -2,8 +2,6 @@ norecursedirs=dist build .tox .eggs addopts=--doctest-modules doctest_optionflags=ALLOW_UNICODE ELLIPSIS -# workaround for warning pytest-dev/pytest#6178 -junit_family=xunit2 filterwarnings= # Suppress deprecation warning in flake8 ignore:SelectableGroups dict interface is deprecated::flake8 diff --git a/setup.cfg b/setup.cfg index 69eb0ee..0f7d652 100644 --- a/setup.cfg +++ b/setup.cfg @@ -28,7 +28,7 @@ exclude = [options.extras_require] testing = # upstream - pytest >= 4.6 + pytest >= 6 pytest-checkdocs >= 2.4 pytest-flake8 pytest-black >= 0.3.7; \ From aae281a9ff6c9a1fa9daad82c79457e8770a1c7e Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Fri, 22 Oct 2021 14:19:58 -0400 Subject: [PATCH 041/176] Remove wheel from build requirements. It's implied for wheel builds. Ref pypa/setuptools#1498. --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 28bd788..190b355 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,5 +1,5 @@ [build-system] -requires = ["setuptools>=56", "wheel", "setuptools_scm[toml]>=3.4.1"] +requires = ["setuptools>=56", "setuptools_scm[toml]>=3.4.1"] build-backend = "setuptools.build_meta" [tool.black] From b519c892cb30321a5012f865ad16c0d86a5b31fb Mon Sep 17 00:00:00 2001 From: Pablo Prietz Date: Tue, 23 Nov 2021 15:40:49 +0100 Subject: [PATCH 042/176] Do not detach kernel drivers on older macOS This avoids root privileges requirement on older macOS (Big Sur and older) --- cuvc.pxd | 10 +++++----- setup.py | 2 +- uvc.pyx | 57 +++++++++++++++++++++++++++++++++++++++++++++++--------- 3 files changed, 54 insertions(+), 15 deletions(-) diff --git a/cuvc.pxd b/cuvc.pxd index 62b13b3..c03ae17 100644 --- a/cuvc.pxd +++ b/cuvc.pxd @@ -381,7 +381,7 @@ cdef extern from "libuvc/libuvc.h": void uvc_unref_device(uvc_device_t *dev) - uvc_error_t uvc_open(uvc_device_t *dev,uvc_device_handle_t **devh) + uvc_error_t uvc_open(uvc_device_t *dev,uvc_device_handle_t **devh, int should_detach_kernel_driver) void uvc_close(uvc_device_handle_t *devh) uvc_device_t *uvc_get_device(uvc_device_handle_t *devh) @@ -396,17 +396,17 @@ cdef extern from "libuvc/libuvc.h": const uvc_extension_unit_t *uvc_get_extension_units(uvc_device_handle_t *devh) - uvc_error_t uvc_get_stream_ctrl_format_size( uvc_device_handle_t *devh, uvc_stream_ctrl_t *ctrl, uvc_frame_format format, int width, int height, int fps) + uvc_error_t uvc_get_stream_ctrl_format_size( uvc_device_handle_t *devh, uvc_stream_ctrl_t *ctrl, uvc_frame_format format, int width, int height, int fps, int should_detach_kernel_driver) uvc_format_desc_t *uvc_get_format_descs(uvc_device_handle_t* ) - uvc_error_t uvc_probe_stream_ctrl( uvc_device_handle_t *devh, uvc_stream_ctrl_t *ctrl) + uvc_error_t uvc_probe_stream_ctrl( uvc_device_handle_t *devh, uvc_stream_ctrl_t *ctrl, int should_detach_kernel_driver) - uvc_error_t uvc_start_streaming( uvc_device_handle_t *devh, uvc_stream_ctrl_t *ctrl, uvc_frame_callback_t *cb, void *user_ptr, uint8_t flags) + uvc_error_t uvc_start_streaming( uvc_device_handle_t *devh, uvc_stream_ctrl_t *ctrl, uvc_frame_callback_t *cb, void *user_ptr, uint8_t flags, int should_detach_kernel_driver) void uvc_stop_streaming(uvc_device_handle_t *devh) - uvc_error_t uvc_stream_open_ctrl(uvc_device_handle_t *devh, uvc_stream_handle_t **strmh, uvc_stream_ctrl_t *ctrl) + uvc_error_t uvc_stream_open_ctrl(uvc_device_handle_t *devh, uvc_stream_handle_t **strmh, uvc_stream_ctrl_t *ctrl, int should_detach_kernel_driver) uvc_error_t set_uvc_stream_ctrl"uvc_stream_ctrl"(uvc_stream_handle_t *strmh, uvc_stream_ctrl_t *ctrl) uvc_error_t uvc_stream_start(uvc_stream_handle_t *strmh,uvc_frame_callback_t *cb,void *user_ptr,float bandwidth_factor, uint8_t flags) #uvc_error_t uvc_stream_start_iso(uvc_stream_handle_t *strmh, uvc_frame_callback_t *cb, void *user_ptr) diff --git a/setup.py b/setup.py index 32b8d8e..7ba3723 100644 --- a/setup.py +++ b/setup.py @@ -75,7 +75,7 @@ setup( name="uvc", - version="0.15.0", + version="0.16.0", description="Usb Video Class Device bindings with format conversion tool.", install_requires=["numpy"], ext_modules=cythonize(extensions), diff --git a/uvc.pyx b/uvc.pyx index 7d3269a..02ce066 100644 --- a/uvc.pyx +++ b/uvc.pyx @@ -16,6 +16,8 @@ cimport numpy as np import numpy as np from cuvc cimport uvc_frame_t, timeval import warnings +import platform +import logging __version__ = "0.15.0" @@ -26,6 +28,40 @@ ELIF UNAME_SYSNAME == "Darwin": ELIF UNAME_SYSNAME == "Linux": include "linux_time.pxi" + +logger = logging.getLogger(__name__) + +# Until version 1.0.24, libusb did not support detaching kernel drivers on macOS. As a +# result, the cameras could only be accessed if no other driver was attached. In macOS +# Monterey, the OS attaches a driver by default. As a result, libusb needs to explicitly +# detach the driver before claiming the device. This is only implemented in newer +# versions of libusb and requires either root privileges or the +# "com.apple.vm.device-access" entitlement. The latter requires a special provisioning +# profile that is not publicly available. https://github.com/libusb/libusb/issues/1014 + +# To avoid requiring root privileges on every macOS version, the code below checks if +# it is running on macOS Big Sur or older and if so, disables the driver detachment +# which corresponds to libusb 1.0.24 behavior, even if libusb 1.0.25 or newer is being +# used. +_mac_version = platform.mac_ver()[0] +if _mac_version: + logger.debug(f"Running on macOS {_mac_version}") + IS_MACOS_BIG_SUR_OR_OLDER = int(_mac_version.split(".")[0]) <= 11 + if IS_MACOS_BIG_SUR_OR_OLDER: + logger.debug( + "Running on macOS Big Sur or older. " + "Will not attempt to detach kernel drivers." + ) + else: + logger.debug( + "Running on macOS Monterey or newer. " + "Requires root privileges to detach kernel drivers." + ) +else: + logger.debug(f"Not running on macOS. Will attempt to detach kernel drivers.") + IS_MACOS_BIG_SUR_OR_OLDER = False +cdef int SHOULD_DETACH_KERNEL_DRIVER = int(not IS_MACOS_BIG_SUR_OR_OLDER) + uvc_error_codes = { 0:"Success (no error)", -1:"Input/output error.", -2:"Invalid parameter.", @@ -75,9 +111,6 @@ class DeviceNotFoundError(InitError): def __init__(self, message): super(InitError, self).__init__(message) self.message = message -#logging -import logging -logger = logging.getLogger(__name__) __version__ = '0.14' #make sure this is the same in setup.py @@ -505,7 +538,7 @@ cdef class Capture: #once found we open the device self.dev = dev - error = uvc.uvc_open(self.dev,&self.devh) + error = uvc.uvc_open(self.dev, &self.devh, SHOULD_DETACH_KERNEL_DRIVER) if error != uvc.UVC_SUCCESS: raise OpenError("could not open device. Error:%s"%uvc_error_codes[error]) logger.debug("Device '%s' opended."%dev_uid) @@ -529,9 +562,15 @@ cdef class Capture: if self._stream_on: self._stop() - status = uvc.uvc_get_stream_ctrl_format_size( self.devh, &self.ctrl, - uvc.UVC_FRAME_FORMAT_COMPRESSED, - mode[0],mode[1],mode[2] ) + status = uvc.uvc_get_stream_ctrl_format_size( + self.devh, + &self.ctrl, + uvc.UVC_FRAME_FORMAT_COMPRESSED, + mode[0], + mode[1], + mode[2], + SHOULD_DETACH_KERNEL_DRIVER, + ) if status != uvc.UVC_SUCCESS: raise InitError("Can't get stream control: Error:'%s'."%uvc_error_codes[status]) self._configured = 1 @@ -542,7 +581,7 @@ cdef class Capture: cdef int status if not self._configured: self._configure_stream() - status = uvc.uvc_stream_open_ctrl(self.devh, &self.strmh, &self.ctrl) + status = uvc.uvc_stream_open_ctrl(self.devh, &self.strmh, &self.ctrl, SHOULD_DETACH_KERNEL_DRIVER) if status != uvc.UVC_SUCCESS: raise InitError("Can't open stream control: Error:'%s'."%uvc_error_codes[status]) status = uvc.uvc_stream_start(self.strmh, NULL, NULL,self._bandwidth_factor,0) @@ -831,7 +870,7 @@ def is_accessible(dev_uid): raise ValueError("Device with uid: '%s' not found"%dev_uid) #once found we open the device - error = uvc.uvc_open(dev,&devh) + error = uvc.uvc_open(dev, &devh, SHOULD_DETACH_KERNEL_DRIVER) if error != uvc.UVC_SUCCESS: uvc.uvc_unref_device(dev) uvc.uvc_exit(ctx) From 4325f5d35fb127d9568d2345025df774dc3adf10 Mon Sep 17 00:00:00 2001 From: Pablo Prietz Date: Wed, 24 Nov 2021 14:55:47 +0100 Subject: [PATCH 043/176] Use uname instead of mac_ver to determine macOS version mac_version is frozen to the bundling os, while uname returns values from the OS running the software. --- uvc.pyx | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/uvc.pyx b/uvc.pyx index 02ce066..279400b 100644 --- a/uvc.pyx +++ b/uvc.pyx @@ -43,10 +43,12 @@ logger = logging.getLogger(__name__) # it is running on macOS Big Sur or older and if so, disables the driver detachment # which corresponds to libusb 1.0.24 behavior, even if libusb 1.0.25 or newer is being # used. -_mac_version = platform.mac_ver()[0] -if _mac_version: - logger.debug(f"Running on macOS {_mac_version}") - IS_MACOS_BIG_SUR_OR_OLDER = int(_mac_version.split(".")[0]) <= 11 +_system_info = platform.uname() +if _system_info.system == "Darwin": + logger.debug(f"Running on macOS (Kernel release {_system_info.release})") + # https://en.wikipedia.org/wiki/Darwin_(operating_system)#Release_history + # macOS Monterey uses Kernel version 21.* + IS_MACOS_BIG_SUR_OR_OLDER = int(_system_info.release.split(".")[0]) < 21 if IS_MACOS_BIG_SUR_OR_OLDER: logger.debug( "Running on macOS Big Sur or older. " From 0019b0af43b9e381e2f0b14753d1bf40ce204490 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Wed, 24 Nov 2021 20:08:49 -0500 Subject: [PATCH 044/176] Require Python 3.7 or later. --- .github/workflows/main.yml | 2 +- setup.cfg | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 6aad7f1..5424298 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -7,7 +7,7 @@ jobs: strategy: matrix: python: - - 3.6 + - 3.7 - 3.9 - "3.10" platform: diff --git a/setup.cfg b/setup.cfg index 0f7d652..bd1da7a 100644 --- a/setup.cfg +++ b/setup.cfg @@ -15,7 +15,7 @@ classifiers = [options] packages = find_namespace: include_package_data = true -python_requires = >=3.6 +python_requires = >=3.7 install_requires = [options.packages.find] From d9008b5c510cd6969127a6a2ab6f832edddef296 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Wed, 24 Nov 2021 20:38:46 -0500 Subject: [PATCH 045/176] Remove filtered warnings, addressed upstream. --- pytest.ini | 4 ---- 1 file changed, 4 deletions(-) diff --git a/pytest.ini b/pytest.ini index 9ecdba4..845aec0 100644 --- a/pytest.ini +++ b/pytest.ini @@ -3,7 +3,3 @@ norecursedirs=dist build .tox .eggs addopts=--doctest-modules doctest_optionflags=ALLOW_UNICODE ELLIPSIS filterwarnings= - # Suppress deprecation warning in flake8 - ignore:SelectableGroups dict interface is deprecated::flake8 - # Suppress deprecation warning in pypa/packaging#433 - ignore:The distutils package is deprecated::packaging.tags From b805634e552f54ebf5924f1fbd66ae6074b40a29 Mon Sep 17 00:00:00 2001 From: Pablo Prietz Date: Thu, 9 Dec 2021 15:41:45 +0100 Subject: [PATCH 046/176] Add name_space folder to src/ and include top-level module files --- setup.cfg | 13 ++++++++----- src/pupil_labs/project_name/__init__.py | 1 + 2 files changed, 9 insertions(+), 5 deletions(-) create mode 100644 src/pupil_labs/project_name/__init__.py diff --git a/setup.cfg b/setup.cfg index bd1da7a..5281e3d 100644 --- a/setup.cfg +++ b/setup.cfg @@ -1,10 +1,10 @@ [metadata] -name = skeleton -author = Jason R. Coombs -author_email = jaraco@jaraco.com -description = skeleton +name = pupil-labs-project-name +author = Pupil Labs GmbH +author_email = info@pupil-labs.com +description = Project description long_description = file:README.rst -url = https://github.com/jaraco/skeleton +url = https://github.com/pupil-labs/python-module-skeleton classifiers = Development Status :: 5 - Production/Stable Intended Audience :: Developers @@ -17,8 +17,11 @@ packages = find_namespace: include_package_data = true python_requires = >=3.7 install_requires = +package_dir = + =src [options.packages.find] +where = src exclude = build* dist* diff --git a/src/pupil_labs/project_name/__init__.py b/src/pupil_labs/project_name/__init__.py new file mode 100644 index 0000000..5a408e3 --- /dev/null +++ b/src/pupil_labs/project_name/__init__.py @@ -0,0 +1 @@ +"""Top-level entry-point for the package""" From b58c2fc809f0f09f387504553b27cd10e2541f1e Mon Sep 17 00:00:00 2001 From: Pablo Prietz Date: Thu, 9 Dec 2021 15:44:41 +0100 Subject: [PATCH 047/176] Add pre-commit hooks --- .pre-commit-config.yaml | 38 ++++++++++++++++++++++++++++++++++---- 1 file changed, 34 insertions(+), 4 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index f66bf56..571c9cc 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,5 +1,35 @@ repos: -- repo: https://github.com/psf/black - rev: 20.8b1 - hooks: - - id: black + - repo: https://github.com/pre-commit/pre-commit-hooks + rev: v4.0.1 + hooks: + - id: check-case-conflict + - id: check-merge-conflict + - id: check-yaml + - id: check-toml + - id: debug-statements + - id: end-of-file-fixer + - id: mixed-line-ending + - id: trailing-whitespace + + - repo: https://github.com/asottile/pyupgrade + rev: v2.29.0 + hooks: + - id: pyupgrade + name: PyUpgrade 3.6+ + args: ["--py36-plus"] + exclude: ^bin/ + + - repo: https://github.com/PyCQA/isort + rev: 5.10.1 + hooks: + - id: isort + + - repo: https://github.com/psf/black + rev: 21.10b0 + hooks: + - id: black + + - repo: https://github.com/asottile/setup-cfg-fmt + rev: v1.19.0 + hooks: + - id: setup-cfg-fmt \ No newline at end of file From 2e49439f162970990e9942751113dddc6d2d13fa Mon Sep 17 00:00:00 2001 From: Pablo Prietz Date: Thu, 9 Dec 2021 15:45:01 +0100 Subject: [PATCH 048/176] Apply pre-commit hooks --- .pre-commit-config.yaml | 2 +- docs/conf.py | 1 - docs/index.rst | 1 - setup.cfg | 73 +++++++++++++++++++---------------------- 4 files changed, 35 insertions(+), 42 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 571c9cc..19dc170 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -32,4 +32,4 @@ repos: - repo: https://github.com/asottile/setup-cfg-fmt rev: v1.19.0 hooks: - - id: setup-cfg-fmt \ No newline at end of file + - id: setup-cfg-fmt diff --git a/docs/conf.py b/docs/conf.py index 4ae7409..7a4197d 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -1,5 +1,4 @@ #!/usr/bin/env python3 -# -*- coding: utf-8 -*- extensions = ['sphinx.ext.autodoc', 'jaraco.packaging.sphinx', 'rst.linker'] diff --git a/docs/index.rst b/docs/index.rst index 325842b..7b71b95 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -19,4 +19,3 @@ Indices and tables * :ref:`genindex` * :ref:`modindex` * :ref:`search` - diff --git a/setup.cfg b/setup.cfg index 5281e3d..c540778 100644 --- a/setup.cfg +++ b/setup.cfg @@ -1,56 +1,51 @@ [metadata] -name = pupil-labs-project-name -author = Pupil Labs GmbH -author_email = info@pupil-labs.com +name = pupil_labs_project_name description = Project description -long_description = file:README.rst +long_description = file: README.rst +long_description_content_type = text/x-rst url = https://github.com/pupil-labs/python-module-skeleton +author = Pupil Labs GmbH +author_email = info@pupil-labs.com +license = MIT +license_file = LICENSE classifiers = - Development Status :: 5 - Production/Stable - Intended Audience :: Developers - License :: OSI Approved :: MIT License - Programming Language :: Python :: 3 - Programming Language :: Python :: 3 :: Only + Development Status :: 5 - Production/Stable + Intended Audience :: Developers + License :: OSI Approved :: MIT License + Programming Language :: Python :: 3 + Programming Language :: Python :: 3 :: Only + Programming Language :: Python :: 3.7 + Programming Language :: Python :: 3.8 + Programming Language :: Python :: 3.9 + Programming Language :: Python :: 3.10 [options] packages = find_namespace: -include_package_data = true python_requires = >=3.7 -install_requires = +include_package_data = true package_dir = =src [options.packages.find] where = src exclude = - build* - dist* - docs* - tests* + build* + dist* + docs* + tests* [options.extras_require] -testing = - # upstream - pytest >= 6 - pytest-checkdocs >= 2.4 - pytest-flake8 - pytest-black >= 0.3.7; \ - # workaround for jaraco/skeleton#22 - python_implementation != "PyPy" - pytest-cov - pytest-mypy; \ - # workaround for jaraco/skeleton#22 - python_implementation != "PyPy" - pytest-enabler >= 1.0.1 - - # local - docs = - # upstream - sphinx - jaraco.packaging >= 8.2 - rst.linker >= 1.9 - - # local - -[options.entry_points] + jaraco.packaging>=8.2 + rst.linker>=1.9 + sphinx +testing = + pytest>=6 + pytest-checkdocs>=2.4 + pytest-cov + pytest-enabler>=1.0.1 + pytest-flake8 + python-implementation!="PyPy" + python-implementation!="PyPy" + pytest-black>=0.3.7;\ + pytest-mypy;\ From 0f4cdc6ee66bf343c988105b5f064622751dbc24 Mon Sep 17 00:00:00 2001 From: Pablo Prietz Date: Thu, 9 Dec 2021 16:25:34 +0100 Subject: [PATCH 049/176] Add gitignore --- .gitignore | 341 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 341 insertions(+) create mode 100644 .gitignore diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..3f0f8bb --- /dev/null +++ b/.gitignore @@ -0,0 +1,341 @@ +version.py + +# Created by https://www.toptal.com/developers/gitignore/api/python,visualstudiocode,macos,windows,linux,pycharm +# Edit at https://www.toptal.com/developers/gitignore?templates=python,visualstudiocode,macos,windows,linux,pycharm + +### Linux ### +*~ + +# temporary files which can be created if a process still has a handle open of a deleted file +.fuse_hidden* + +# KDE directory preferences +.directory + +# Linux trash folder which might appear on any partition or disk +.Trash-* + +# .nfs files are created when an open file is removed but is still being accessed +.nfs* + +### macOS ### +# General +.DS_Store +.AppleDouble +.LSOverride + +# Icon must end with two \r +Icon + + +# Thumbnails +._* + +# Files that might appear in the root of a volume +.DocumentRevisions-V100 +.fseventsd +.Spotlight-V100 +.TemporaryItems +.Trashes +.VolumeIcon.icns +.com.apple.timemachine.donotpresent + +# Directories potentially created on remote AFP share +.AppleDB +.AppleDesktop +Network Trash Folder +Temporary Items +.apdisk + +### PyCharm ### +# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider +# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 + +# User-specific stuff +.idea/**/workspace.xml +.idea/**/tasks.xml +.idea/**/usage.statistics.xml +.idea/**/dictionaries +.idea/**/shelf + +# AWS User-specific +.idea/**/aws.xml + +# Generated files +.idea/**/contentModel.xml + +# Sensitive or high-churn files +.idea/**/dataSources/ +.idea/**/dataSources.ids +.idea/**/dataSources.local.xml +.idea/**/sqlDataSources.xml +.idea/**/dynamic.xml +.idea/**/uiDesigner.xml +.idea/**/dbnavigator.xml + +# Gradle +.idea/**/gradle.xml +.idea/**/libraries + +# Gradle and Maven with auto-import +# When using Gradle or Maven with auto-import, you should exclude module files, +# since they will be recreated, and may cause churn. Uncomment if using +# auto-import. +# .idea/artifacts +# .idea/compiler.xml +# .idea/jarRepositories.xml +# .idea/modules.xml +# .idea/*.iml +# .idea/modules +# *.iml +# *.ipr + +# CMake +cmake-build-*/ + +# Mongo Explorer plugin +.idea/**/mongoSettings.xml + +# File-based project format +*.iws + +# IntelliJ +out/ + +# mpeltonen/sbt-idea plugin +.idea_modules/ + +# JIRA plugin +atlassian-ide-plugin.xml + +# Cursive Clojure plugin +.idea/replstate.xml + +# Crashlytics plugin (for Android Studio and IntelliJ) +com_crashlytics_export_strings.xml +crashlytics.properties +crashlytics-build.properties +fabric.properties + +# Editor-based Rest Client +.idea/httpRequests + +# Android studio 3.1+ serialized cache file +.idea/caches/build_file_checksums.ser + +### PyCharm Patch ### +# Comment Reason: https://github.com/joeblau/gitignore.io/issues/186#issuecomment-215987721 + +# *.iml +# modules.xml +# .idea/misc.xml +# *.ipr + +# Sonarlint plugin +# https://plugins.jetbrains.com/plugin/7973-sonarlint +.idea/**/sonarlint/ + +# SonarQube Plugin +# https://plugins.jetbrains.com/plugin/7238-sonarqube-community-plugin +.idea/**/sonarIssues.xml + +# Markdown Navigator plugin +# https://plugins.jetbrains.com/plugin/7896-markdown-navigator-enhanced +.idea/**/markdown-navigator.xml +.idea/**/markdown-navigator-enh.xml +.idea/**/markdown-navigator/ + +# Cache file creation bug +# See https://youtrack.jetbrains.com/issue/JBR-2257 +.idea/$CACHE_FILE$ + +# CodeStream plugin +# https://plugins.jetbrains.com/plugin/12206-codestream +.idea/codestream.xml + +### Python ### +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] +*$py.class + +# C extensions +*.so + +# Distribution / packaging +.Python +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +wheels/ +share/python-wheels/ +*.egg-info/ +.installed.cfg +*.egg +MANIFEST + +# PyInstaller +# Usually these files are written by a python script from a template +# before PyInstaller builds the exe, so as to inject date/other infos into it. +*.manifest +*.spec + +# Installer logs +pip-log.txt +pip-delete-this-directory.txt + +# Unit test / coverage reports +htmlcov/ +.tox/ +.nox/ +.coverage +.coverage.* +.cache +nosetests.xml +coverage.xml +*.cover +*.py,cover +.hypothesis/ +.pytest_cache/ +cover/ + +# Translations +*.mo +*.pot + +# Django stuff: +*.log +local_settings.py +db.sqlite3 +db.sqlite3-journal + +# Flask stuff: +instance/ +.webassets-cache + +# Scrapy stuff: +.scrapy + +# Sphinx documentation +docs/_build/ + +# PyBuilder +.pybuilder/ +target/ + +# Jupyter Notebook +.ipynb_checkpoints + +# IPython +profile_default/ +ipython_config.py + +# pyenv +# For a library or package, you might want to ignore these files since the code is +# intended to run in multiple environments; otherwise, check them in: +# .python-version + +# pipenv +# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. +# However, in case of collaboration, if having platform-specific dependencies or dependencies +# having no cross-platform support, pipenv may install dependencies that don't work, or not +# install all needed dependencies. +#Pipfile.lock + +# PEP 582; used by e.g. github.com/David-OConnor/pyflow +__pypackages__/ + +# Celery stuff +celerybeat-schedule +celerybeat.pid + +# SageMath parsed files +*.sage.py + +# Environments +.env +.venv +env/ +venv/ +ENV/ +env.bak/ +venv.bak/ + +# Spyder project settings +.spyderproject +.spyproject + +# Rope project settings +.ropeproject + +# mkdocs documentation +/site + +# mypy +.mypy_cache/ +.dmypy.json +dmypy.json + +# Pyre type checker +.pyre/ + +# pytype static type analyzer +.pytype/ + +# Cython debug symbols +cython_debug/ + +### VisualStudioCode ### +.vscode/* +!.vscode/settings.json +!.vscode/tasks.json +!.vscode/launch.json +!.vscode/extensions.json +*.code-workspace + +# Local History for Visual Studio Code +.history/ + +### VisualStudioCode Patch ### +# Ignore all local history of files +.history +.ionide + +# Support for Project snippet scope +!.vscode/*.code-snippets + +### Windows ### +# Windows thumbnail cache files +Thumbs.db +Thumbs.db:encryptable +ehthumbs.db +ehthumbs_vista.db + +# Dump file +*.stackdump + +# Folder config file +[Dd]esktop.ini + +# Recycle Bin used on file shares +$RECYCLE.BIN/ + +# Windows Installer files +*.cab +*.msi +*.msix +*.msm +*.msp + +# Windows shortcuts +*.lnk + +# End of https://www.toptal.com/developers/gitignore/api/python,visualstudiocode,macos,windows,linux,pycharm From d7fe850c217987e2846896012df3ab97b3d57172 Mon Sep 17 00:00:00 2001 From: Pablo Prietz Date: Thu, 9 Dec 2021 16:27:08 +0100 Subject: [PATCH 050/176] Generate version.py on install --- pyproject.toml | 8 +++++++- setup.cfg | 6 ++---- src/pupil_labs/project_name/__init__.py | 3 +++ tests/test_api.py | 6 ++++++ tox.ini | 4 ++-- 5 files changed, 20 insertions(+), 7 deletions(-) create mode 100644 tests/test_api.py diff --git a/pyproject.toml b/pyproject.toml index 190b355..458b6f5 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,11 +1,17 @@ [build-system] -requires = ["setuptools>=56", "setuptools_scm[toml]>=3.4.1"] +requires = ["setuptools>=56", "wheel", "setuptools_scm[toml]>=3.4.1"] build-backend = "setuptools.build_meta" [tool.black] skip-string-normalization = true [tool.setuptools_scm] +write_to = "src/pupil_labs/project_name/version.py" +write_to_template = """ +\"\"\" Version information \"\"\" +__version__ = "{version}" +__version_info__ = {version_tuple} +""" [pytest.enabler.black] addopts = "--black" diff --git a/setup.cfg b/setup.cfg index c540778..2eb1bdf 100644 --- a/setup.cfg +++ b/setup.cfg @@ -45,7 +45,5 @@ testing = pytest-cov pytest-enabler>=1.0.1 pytest-flake8 - python-implementation!="PyPy" - python-implementation!="PyPy" - pytest-black>=0.3.7;\ - pytest-mypy;\ + pytest-black>=0.3.7 + pytest-mypy diff --git a/src/pupil_labs/project_name/__init__.py b/src/pupil_labs/project_name/__init__.py index 5a408e3..b9f1432 100644 --- a/src/pupil_labs/project_name/__init__.py +++ b/src/pupil_labs/project_name/__init__.py @@ -1 +1,4 @@ """Top-level entry-point for the package""" + +# .version is generated on install via setuptools_scm, see pyproject.toml +from .version import __version__, __version_info__ diff --git a/tests/test_api.py b/tests/test_api.py new file mode 100644 index 0000000..24c0a5b --- /dev/null +++ b/tests/test_api.py @@ -0,0 +1,6 @@ +import pupil_labs.project_name as this_project + + +def test_package_metadata() -> None: + assert hasattr(this_project, "__version__") + assert hasattr(this_project, "__version_info__") diff --git a/tox.ini b/tox.ini index 3ca2af3..5bf4e06 100644 --- a/tox.ini +++ b/tox.ini @@ -7,9 +7,9 @@ toxworkdir={env:TOX_WORK_DIR:.tox} [testenv] -deps = +deps = pytest commands = - pytest {posargs} + pytest tests {posargs} usedevelop = True extras = testing From 84d112e569b81ae5c0b28ec7b4b364f51fb93993 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Thu, 9 Dec 2021 15:32:45 +0000 Subject: [PATCH 051/176] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- setup.cfg | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.cfg b/setup.cfg index 2eb1bdf..c2428b0 100644 --- a/setup.cfg +++ b/setup.cfg @@ -41,9 +41,9 @@ docs = sphinx testing = pytest>=6 + pytest-black>=0.3.7 pytest-checkdocs>=2.4 pytest-cov pytest-enabler>=1.0.1 pytest-flake8 - pytest-black>=0.3.7 pytest-mypy From 3782a1161028588c95e1e892f1fdbdbd65c09265 Mon Sep 17 00:00:00 2001 From: Pablo Prietz Date: Fri, 17 Dec 2021 14:30:37 +0100 Subject: [PATCH 052/176] pre-commit: Use black profile for isort --- .pre-commit-config.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 19dc170..327af9e 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -23,6 +23,7 @@ repos: rev: 5.10.1 hooks: - id: isort + args: [--profile, black] - repo: https://github.com/psf/black rev: 21.10b0 From eca1c4ca6e104c8add280c721cbb365196f55ac7 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Wed, 24 Nov 2021 20:38:46 -0500 Subject: [PATCH 053/176] Remove filtered warnings, addressed upstream. --- pytest.ini | 2 -- 1 file changed, 2 deletions(-) diff --git a/pytest.ini b/pytest.ini index 9ecdba4..ec965b2 100644 --- a/pytest.ini +++ b/pytest.ini @@ -5,5 +5,3 @@ doctest_optionflags=ALLOW_UNICODE ELLIPSIS filterwarnings= # Suppress deprecation warning in flake8 ignore:SelectableGroups dict interface is deprecated::flake8 - # Suppress deprecation warning in pypa/packaging#433 - ignore:The distutils package is deprecated::packaging.tags From 08e81f4868812deaf2e5f261ee106640e5986276 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 3 Jan 2022 20:01:06 +0000 Subject: [PATCH 054/176] [pre-commit.ci] pre-commit autoupdate MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit updates: - [github.com/pre-commit/pre-commit-hooks: v4.0.1 → v4.1.0](https://github.com/pre-commit/pre-commit-hooks/compare/v4.0.1...v4.1.0) - [github.com/asottile/pyupgrade: v2.29.0 → v2.31.0](https://github.com/asottile/pyupgrade/compare/v2.29.0...v2.31.0) - [github.com/psf/black: 21.10b0 → 21.12b0](https://github.com/psf/black/compare/21.10b0...21.12b0) - [github.com/asottile/setup-cfg-fmt: v1.19.0 → v1.20.0](https://github.com/asottile/setup-cfg-fmt/compare/v1.19.0...v1.20.0) --- .pre-commit-config.yaml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 327af9e..217cae7 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,6 +1,6 @@ repos: - repo: https://github.com/pre-commit/pre-commit-hooks - rev: v4.0.1 + rev: v4.1.0 hooks: - id: check-case-conflict - id: check-merge-conflict @@ -12,7 +12,7 @@ repos: - id: trailing-whitespace - repo: https://github.com/asottile/pyupgrade - rev: v2.29.0 + rev: v2.31.0 hooks: - id: pyupgrade name: PyUpgrade 3.6+ @@ -26,11 +26,11 @@ repos: args: [--profile, black] - repo: https://github.com/psf/black - rev: 21.10b0 + rev: 21.12b0 hooks: - id: black - repo: https://github.com/asottile/setup-cfg-fmt - rev: v1.19.0 + rev: v1.20.0 hooks: - id: setup-cfg-fmt From 7e262a416794f5d056eaebfaf9b6f2c078001f68 Mon Sep 17 00:00:00 2001 From: Pablo Prietz Date: Fri, 7 Jan 2022 14:41:30 +0100 Subject: [PATCH 055/176] Add Python 3.8 tests to GA --- .github/workflows/main.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 5424298..1e31151 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -8,6 +8,7 @@ jobs: matrix: python: - 3.7 + - 3.8 - 3.9 - "3.10" platform: From ec37115ee9b8b8d3f419e203fcc1d972ceb103d1 Mon Sep 17 00:00:00 2001 From: Pablo Prietz Date: Thu, 20 Jan 2022 11:58:31 +0100 Subject: [PATCH 056/176] Add flake8<4 constraint to avoid https://github.com/tholo/pytest-flake8/issues/81 --- setup.cfg | 1 + 1 file changed, 1 insertion(+) diff --git a/setup.cfg b/setup.cfg index c2428b0..e7419ed 100644 --- a/setup.cfg +++ b/setup.cfg @@ -40,6 +40,7 @@ docs = rst.linker>=1.9 sphinx testing = + flake8<4 # workaround https://github.com/tholo/pytest-flake8/issues/81 pytest>=6 pytest-black>=0.3.7 pytest-checkdocs>=2.4 From 86d90f63404fbe8fe18dfd14c09064138dbbf15b Mon Sep 17 00:00:00 2001 From: Pablo Prietz Date: Thu, 20 Jan 2022 12:03:31 +0100 Subject: [PATCH 057/176] Add sphinx<4.4 constraint to correctly document TypeVars --- setup.cfg | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.cfg b/setup.cfg index e7419ed..423a3eb 100644 --- a/setup.cfg +++ b/setup.cfg @@ -38,7 +38,7 @@ exclude = docs = jaraco.packaging>=8.2 rst.linker>=1.9 - sphinx + sphinx<4.4 # 4.4 does not detect TypeVars correctly testing = flake8<4 # workaround https://github.com/tholo/pytest-flake8/issues/81 pytest>=6 From 903e4d986fcd486d3afbe6f315d38e5807fa7af8 Mon Sep 17 00:00:00 2001 From: Pablo Prietz Date: Thu, 20 Jan 2022 12:05:29 +0100 Subject: [PATCH 058/176] Run flake8 as part of the pre-commit hooks. Use same version as in "testing" extra install --- .pre-commit-config.yaml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 217cae7..fe9b902 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -19,6 +19,11 @@ repos: args: ["--py36-plus"] exclude: ^bin/ + - repo: https://github.com/pycqa/flake8 + rev: 3.9.2 + hooks: + - id: flake8 + - repo: https://github.com/PyCQA/isort rev: 5.10.1 hooks: From d1fcc970f86921c7ad5084e31d17906bf58102ef Mon Sep 17 00:00:00 2001 From: Pablo Prietz Date: Thu, 20 Jan 2022 12:07:07 +0100 Subject: [PATCH 059/176] Take over Pupil Cloud flake8 config --- .flake8 | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/.flake8 b/.flake8 index 48b2e24..52378bc 100644 --- a/.flake8 +++ b/.flake8 @@ -1,9 +1,16 @@ [flake8] max-line-length = 88 -# jaraco/skeleton#34 -max-complexity = 10 +max-complexity = 18 extend-ignore = # Black creates whitespace before colon E203 + W503 + F541 + +extend-select = B,C,E,F,W,T4,B9 +exclude = + .venv + .pyenv + __pycache__ From 33527acb79a50a3ccf45ef6828e5653931021588 Mon Sep 17 00:00:00 2001 From: Pablo Prietz Date: Thu, 20 Jan 2022 12:08:01 +0100 Subject: [PATCH 060/176] Fix flake8 warning about unused import --- src/pupil_labs/project_name/__init__.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/pupil_labs/project_name/__init__.py b/src/pupil_labs/project_name/__init__.py index b9f1432..b94cfaf 100644 --- a/src/pupil_labs/project_name/__init__.py +++ b/src/pupil_labs/project_name/__init__.py @@ -2,3 +2,5 @@ # .version is generated on install via setuptools_scm, see pyproject.toml from .version import __version__, __version_info__ + +__all__ = ["__version__", "__version_info__"] From 42e2a549126d0fc3839ba270fbf8d45af9397f68 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 31 Jan 2022 21:01:15 +0000 Subject: [PATCH 061/176] [pre-commit.ci] pre-commit autoupdate MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit updates: - [github.com/pycqa/flake8: 3.9.2 → 4.0.1](https://github.com/pycqa/flake8/compare/3.9.2...4.0.1) - [github.com/psf/black: 21.12b0 → 22.1.0](https://github.com/psf/black/compare/21.12b0...22.1.0) --- .pre-commit-config.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index fe9b902..2b47a42 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -20,7 +20,7 @@ repos: exclude: ^bin/ - repo: https://github.com/pycqa/flake8 - rev: 3.9.2 + rev: 4.0.1 hooks: - id: flake8 @@ -31,7 +31,7 @@ repos: args: [--profile, black] - repo: https://github.com/psf/black - rev: 21.12b0 + rev: 22.1.0 hooks: - id: black From 4f9825dafa8d13a5f8b8bd8eb8bfc6414329cb18 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Tue, 1 Feb 2022 04:09:17 -0500 Subject: [PATCH 062/176] Update badge year --- README.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.rst b/README.rst index a3e1b74..c82c642 100644 --- a/README.rst +++ b/README.rst @@ -17,5 +17,5 @@ .. .. image:: https://readthedocs.org/projects/skeleton/badge/?version=latest .. :target: https://skeleton.readthedocs.io/en/latest/?badge=latest -.. image:: https://img.shields.io/badge/skeleton-2021-informational +.. image:: https://img.shields.io/badge/skeleton-2022-informational :target: https://blog.jaraco.com/skeleton From 7e01b721c237ee4947e3b9d6e56bb03a028f3f6a Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Tue, 1 Feb 2022 04:11:54 -0500 Subject: [PATCH 063/176] Remove setup.py, no longer needed. --- setup.py | 6 ------ 1 file changed, 6 deletions(-) delete mode 100644 setup.py diff --git a/setup.py b/setup.py deleted file mode 100644 index bac24a4..0000000 --- a/setup.py +++ /dev/null @@ -1,6 +0,0 @@ -#!/usr/bin/env python - -import setuptools - -if __name__ == "__main__": - setuptools.setup() From 8949d1a1169c9271ceb8aab3f1deea9d82e2fa0d Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Fri, 4 Feb 2022 21:45:52 -0500 Subject: [PATCH 064/176] Add exclusions for pytest 7 deprecations in plugins. Fixes jaraco/skeleton#57. --- pytest.ini | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/pytest.ini b/pytest.ini index ec965b2..52f19be 100644 --- a/pytest.ini +++ b/pytest.ini @@ -5,3 +5,14 @@ doctest_optionflags=ALLOW_UNICODE ELLIPSIS filterwarnings= # Suppress deprecation warning in flake8 ignore:SelectableGroups dict interface is deprecated::flake8 + + # shopkeep/pytest-black#55 + ignore: is not using a cooperative constructor:pytest.PytestDeprecationWarning + ignore:The \(fspath. py.path.local\) argument to BlackItem is deprecated.:pytest.PytestRemovedIn8Warning + + # tholo/pytest-flake8#83 + ignore: is not using a cooperative constructor:pytest.PytestDeprecationWarning + ignore:The \(fspath. py.path.local\) argument to Flake8Item is deprecated.:pytest.PytestRemovedIn8Warning + + # dbader/pytest-mypy#131 + ignore:The \(fspath. py.path.local\) argument to MypyFile is deprecated.:pytest.PytestRemovedIn8Warning From badffe9af9b79dff781f6768bcf48fbd8abd0945 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sat, 5 Feb 2022 14:29:09 -0500 Subject: [PATCH 065/176] Use the parent category PytestDeprecationWarning, which is available on older pytest versions. Fixes jaraco/skeleton#57. --- pytest.ini | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pytest.ini b/pytest.ini index 52f19be..cbbe3b1 100644 --- a/pytest.ini +++ b/pytest.ini @@ -8,11 +8,11 @@ filterwarnings= # shopkeep/pytest-black#55 ignore: is not using a cooperative constructor:pytest.PytestDeprecationWarning - ignore:The \(fspath. py.path.local\) argument to BlackItem is deprecated.:pytest.PytestRemovedIn8Warning + ignore:The \(fspath. py.path.local\) argument to BlackItem is deprecated.:pytest.PytestDeprecationWarning # tholo/pytest-flake8#83 ignore: is not using a cooperative constructor:pytest.PytestDeprecationWarning - ignore:The \(fspath. py.path.local\) argument to Flake8Item is deprecated.:pytest.PytestRemovedIn8Warning + ignore:The \(fspath. py.path.local\) argument to Flake8Item is deprecated.:pytest.PytestDeprecationWarning # dbader/pytest-mypy#131 - ignore:The \(fspath. py.path.local\) argument to MypyFile is deprecated.:pytest.PytestRemovedIn8Warning + ignore:The \(fspath. py.path.local\) argument to MypyFile is deprecated.:pytest.PytestDeprecationWarning From 96ea56305df99a3c13334d42ea45f779cab2c505 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Thu, 10 Feb 2022 20:36:16 -0500 Subject: [PATCH 066/176] Bump pytest-mypy and remove workaround for dbader/pytest-mypy#131. --- pytest.ini | 3 --- setup.cfg | 2 +- 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/pytest.ini b/pytest.ini index cbbe3b1..b6880c8 100644 --- a/pytest.ini +++ b/pytest.ini @@ -13,6 +13,3 @@ filterwarnings= # tholo/pytest-flake8#83 ignore: is not using a cooperative constructor:pytest.PytestDeprecationWarning ignore:The \(fspath. py.path.local\) argument to Flake8Item is deprecated.:pytest.PytestDeprecationWarning - - # dbader/pytest-mypy#131 - ignore:The \(fspath. py.path.local\) argument to MypyFile is deprecated.:pytest.PytestDeprecationWarning diff --git a/setup.cfg b/setup.cfg index bd1da7a..1b048af 100644 --- a/setup.cfg +++ b/setup.cfg @@ -35,7 +35,7 @@ testing = # workaround for jaraco/skeleton#22 python_implementation != "PyPy" pytest-cov - pytest-mypy; \ + pytest-mypy >= 0.9.1; \ # workaround for jaraco/skeleton#22 python_implementation != "PyPy" pytest-enabler >= 1.0.1 From a9ea801a43fc62a569cf60e1c28e477ba510d8a0 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Thu, 10 Feb 2022 21:58:57 -0500 Subject: [PATCH 067/176] Require jaraco.packaging 9 adding compatibility for projects with no setup.py file. --- setup.cfg | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.cfg b/setup.cfg index 1b048af..3b7ac30 100644 --- a/setup.cfg +++ b/setup.cfg @@ -45,7 +45,7 @@ testing = docs = # upstream sphinx - jaraco.packaging >= 8.2 + jaraco.packaging >= 9 rst.linker >= 1.9 # local From f22eb5b60adbe158e458614ea0380a9071c39347 Mon Sep 17 00:00:00 2001 From: Anderson Bravalheri Date: Sat, 12 Feb 2022 09:50:31 +0000 Subject: [PATCH 068/176] Ignore flake8/black warnings with pytest 7.0.1 (jaraco/skeleton#58) --- pytest.ini | 2 ++ 1 file changed, 2 insertions(+) diff --git a/pytest.ini b/pytest.ini index b6880c8..80e98cc 100644 --- a/pytest.ini +++ b/pytest.ini @@ -9,7 +9,9 @@ filterwarnings= # shopkeep/pytest-black#55 ignore: is not using a cooperative constructor:pytest.PytestDeprecationWarning ignore:The \(fspath. py.path.local\) argument to BlackItem is deprecated.:pytest.PytestDeprecationWarning + ignore:BlackItem is an Item subclass and should not be a collector:pytest.PytestWarning # tholo/pytest-flake8#83 ignore: is not using a cooperative constructor:pytest.PytestDeprecationWarning ignore:The \(fspath. py.path.local\) argument to Flake8Item is deprecated.:pytest.PytestDeprecationWarning + ignore:Flake8Item is an Item subclass and should not be a collector:pytest.PytestWarning From fa620c002660cc08d3d3cb47842e3b2bfa173210 Mon Sep 17 00:00:00 2001 From: Pablo Prietz Date: Thu, 17 Feb 2022 09:24:53 +0100 Subject: [PATCH 069/176] Run flake8 only via pre-commit Avoids https://github.com/tholo/pytest-flake8/issues/81 when using flake8 through pytest-flake8 --- pyproject.toml | 3 --- setup.cfg | 2 -- 2 files changed, 5 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 458b6f5..11006f5 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -19,8 +19,5 @@ addopts = "--black" [pytest.enabler.mypy] addopts = "--mypy" -[pytest.enabler.flake8] -addopts = "--flake8" - [pytest.enabler.cov] addopts = "--cov" diff --git a/setup.cfg b/setup.cfg index 423a3eb..1acbce4 100644 --- a/setup.cfg +++ b/setup.cfg @@ -40,11 +40,9 @@ docs = rst.linker>=1.9 sphinx<4.4 # 4.4 does not detect TypeVars correctly testing = - flake8<4 # workaround https://github.com/tholo/pytest-flake8/issues/81 pytest>=6 pytest-black>=0.3.7 pytest-checkdocs>=2.4 pytest-cov pytest-enabler>=1.0.1 - pytest-flake8 pytest-mypy From 04fe68a96ee8e3d3ca521b4abbfe53203063f9d9 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sat, 19 Feb 2022 21:14:39 -0500 Subject: [PATCH 070/176] Ran pre-commit autoupdate --- .pre-commit-config.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index f66bf56..edf6f55 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,5 +1,5 @@ repos: - repo: https://github.com/psf/black - rev: 20.8b1 + rev: 22.1.0 hooks: - id: black From a1199d8a7ddad26e151682fdaf993d2a1714d1dc Mon Sep 17 00:00:00 2001 From: Pablo Prietz Date: Fri, 25 Feb 2022 13:46:30 +0100 Subject: [PATCH 071/176] Read package version using importlib --- pyproject.toml | 6 ------ setup.cfg | 2 ++ src/pupil_labs/project_name/__init__.py | 14 +++++++++++--- tests/test_api.py | 1 - 4 files changed, 13 insertions(+), 10 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 11006f5..c8b8cf5 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -6,12 +6,6 @@ build-backend = "setuptools.build_meta" skip-string-normalization = true [tool.setuptools_scm] -write_to = "src/pupil_labs/project_name/version.py" -write_to_template = """ -\"\"\" Version information \"\"\" -__version__ = "{version}" -__version_info__ = {version_tuple} -""" [pytest.enabler.black] addopts = "--black" diff --git a/setup.cfg b/setup.cfg index 1acbce4..732be02 100644 --- a/setup.cfg +++ b/setup.cfg @@ -20,6 +20,8 @@ classifiers = Programming Language :: Python :: 3.10 [options] +install_requires = + importlib-metadata;python_version<"3.8" packages = find_namespace: python_requires = >=3.7 include_package_data = true diff --git a/src/pupil_labs/project_name/__init__.py b/src/pupil_labs/project_name/__init__.py index b94cfaf..1551b7c 100644 --- a/src/pupil_labs/project_name/__init__.py +++ b/src/pupil_labs/project_name/__init__.py @@ -1,6 +1,14 @@ """Top-level entry-point for the package""" -# .version is generated on install via setuptools_scm, see pyproject.toml -from .version import __version__, __version_info__ +try: + from importlib.metadata import PackageNotFoundError, version +except ImportError: + from importlib_metadata import PackageNotFoundError, version -__all__ = ["__version__", "__version_info__"] +try: + __version__ = version("pupil_labs.project_name") +except PackageNotFoundError: + # package is not installed + pass + +__all__ = ["__version__"] diff --git a/tests/test_api.py b/tests/test_api.py index 24c0a5b..f7ef4bd 100644 --- a/tests/test_api.py +++ b/tests/test_api.py @@ -3,4 +3,3 @@ def test_package_metadata() -> None: assert hasattr(this_project, "__version__") - assert hasattr(this_project, "__version_info__") From f80824b178e10ee3ea7bea121933930b23336b5b Mon Sep 17 00:00:00 2001 From: Pablo Prietz Date: Fri, 25 Feb 2022 13:46:59 +0100 Subject: [PATCH 072/176] Do not check black format via pytest pre-commit takes care of that --- pyproject.toml | 3 --- setup.cfg | 1 - 2 files changed, 4 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index c8b8cf5..95477f7 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -7,9 +7,6 @@ skip-string-normalization = true [tool.setuptools_scm] -[pytest.enabler.black] -addopts = "--black" - [pytest.enabler.mypy] addopts = "--mypy" diff --git a/setup.cfg b/setup.cfg index 732be02..73df7ff 100644 --- a/setup.cfg +++ b/setup.cfg @@ -43,7 +43,6 @@ docs = sphinx<4.4 # 4.4 does not detect TypeVars correctly testing = pytest>=6 - pytest-black>=0.3.7 pytest-checkdocs>=2.4 pytest-cov pytest-enabler>=1.0.1 From d60e54ada02d0eb78b98aa73700a608f242943cf Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Fri, 25 Feb 2022 12:48:47 +0000 Subject: [PATCH 073/176] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- setup.cfg | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.cfg b/setup.cfg index 73df7ff..63a9877 100644 --- a/setup.cfg +++ b/setup.cfg @@ -20,9 +20,9 @@ classifiers = Programming Language :: Python :: 3.10 [options] +packages = find_namespace: install_requires = importlib-metadata;python_version<"3.8" -packages = find_namespace: python_requires = >=3.7 include_package_data = true package_dir = From fc99c805fef6306ebf6caf4151919b2bd7cc76d1 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 14 Mar 2022 22:17:48 +0000 Subject: [PATCH 074/176] [pre-commit.ci] pre-commit autoupdate MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit updates: - [github.com/asottile/pyupgrade: v2.31.0 → v2.31.1](https://github.com/asottile/pyupgrade/compare/v2.31.0...v2.31.1) --- .pre-commit-config.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 2b47a42..0bb43a9 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -12,7 +12,7 @@ repos: - id: trailing-whitespace - repo: https://github.com/asottile/pyupgrade - rev: v2.31.0 + rev: v2.31.1 hooks: - id: pyupgrade name: PyUpgrade 3.6+ From 1a6b828304e7a8896b55d9ebf83f481ba7ebd568 Mon Sep 17 00:00:00 2001 From: Sviatoslav Sydorenko Date: Fri, 22 Apr 2022 17:43:46 +0200 Subject: [PATCH 075/176] Inject check job into CI workflow as ultimate flag (#55) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This patch adds a job that is able to accurately signal whether all the expectations of the required jobs to succeed are met. This job can then be used as a source of truth for judging whether "CI passes" and can be used in the branch protection. It also plays a role of a convenient "gate" — this is the only job that would have to be listed in the branch protection as opposed to listing every single job name generated by the test matrix (and they all have different names — it's not possible to just select one `test` job name). Ref: https://github.com/re-actors/alls-green#why --- .github/workflows/main.yml | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 5424298..b54fd6a 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -27,8 +27,23 @@ jobs: - name: Run tests run: tox + check: # This job does nothing and is only used for the branch protection + if: always() + + needs: + - test + + runs-on: ubuntu-latest + + steps: + - name: Decide whether the needed jobs succeeded or failed + uses: re-actors/alls-green@release/v1 + with: + jobs: ${{ toJSON(needs) }} + release: - needs: test + needs: + - check if: github.event_name == 'push' && contains(github.ref, 'refs/tags/') runs-on: ubuntu-latest From 10bf1b1fb9e09e9836bea9e2edec620cd9eea7f9 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sat, 4 Jun 2022 21:37:40 -0400 Subject: [PATCH 076/176] Add Python 3.11 into the matrix using workaround from actions/setup-python#213. Drop 3.9 from matrix for efficiency. --- .github/workflows/main.yml | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index b54fd6a..6468ee0 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -7,9 +7,11 @@ jobs: strategy: matrix: python: - - 3.7 - - 3.9 - - "3.10" + # Build on pre-releases until stable, then stable releases. + # actions/setup-python#213 + - ~3.7.0-0 + - ~3.10.0-0 + - ~3.11.0-0 platform: - ubuntu-latest - macos-latest From a4f5b769793af19f7b858816889c1bf026f55f5c Mon Sep 17 00:00:00 2001 From: Hugo van Kemenade Date: Sun, 5 Jun 2022 04:47:15 +0300 Subject: [PATCH 077/176] Update base URL for PEPs (#61) --- docs/conf.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/conf.py b/docs/conf.py index 4ae7409..319b138 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -19,7 +19,7 @@ ), dict( pattern=r'PEP[- ](?P\d+)', - url='https://www.python.org/dev/peps/pep-{pep_number:0>4}/', + url='https://peps.python.org/pep-{pep_number:0>4}/', ), ], ) From 74f337fec4c233b3a6750fa64b61d03c189d9416 Mon Sep 17 00:00:00 2001 From: Anderson Bravalheri Date: Sun, 5 Jun 2022 02:50:24 +0100 Subject: [PATCH 078/176] Update Github actions to v3 (#62) --- .github/workflows/main.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 6468ee0..948da05 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -18,9 +18,9 @@ jobs: - windows-latest runs-on: ${{ matrix.platform }} steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - name: Setup Python - uses: actions/setup-python@v2 + uses: actions/setup-python@v3 with: python-version: ${{ matrix.python }} - name: Install tox @@ -50,9 +50,9 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - name: Setup Python - uses: actions/setup-python@v2 + uses: actions/setup-python@v3 with: python-version: "3.10" - name: Install tox From e719f86c138a750f0c4599cd01cb8067b1ca95c8 Mon Sep 17 00:00:00 2001 From: wim glenn Date: Sun, 5 Jun 2022 15:01:02 -0500 Subject: [PATCH 079/176] exclude build env from cov reporting (#60) * Update .coveragerc * Keep whitespace consistent. Co-authored-by: Jason R. Coombs --- .coveragerc | 1 + 1 file changed, 1 insertion(+) diff --git a/.coveragerc b/.coveragerc index 6a34e66..01164f6 100644 --- a/.coveragerc +++ b/.coveragerc @@ -2,6 +2,7 @@ omit = # leading `*/` for pytest-dev/pytest-cov#456 */.tox/* + */pep517-build-env-* [report] show_missing = True From 6dcd157a7057ec8e1f1f6afebe2115f55df4aaed Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Wed, 15 Jun 2022 20:57:40 -0400 Subject: [PATCH 080/176] Prefer spaces for rst. Fixes jaraco/skeleton#64. --- .editorconfig | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.editorconfig b/.editorconfig index b8aeea1..304196f 100644 --- a/.editorconfig +++ b/.editorconfig @@ -14,3 +14,6 @@ max_line_length = 88 [*.{yml,yaml}] indent_style = space indent_size = 2 + +[*.rst] +indent_style = space From 2678a7e82d581c07691575d90cd255b64ee63a27 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Wed, 22 Jun 2022 15:56:54 -0400 Subject: [PATCH 081/176] Honor PEP 518 with pytest-enabler. --- pyproject.toml | 8 ++++---- setup.cfg | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 190b355..60de242 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -7,14 +7,14 @@ skip-string-normalization = true [tool.setuptools_scm] -[pytest.enabler.black] +[tool.pytest-enabler.black] addopts = "--black" -[pytest.enabler.mypy] +[tool.pytest-enabler.mypy] addopts = "--mypy" -[pytest.enabler.flake8] +[tool.pytest-enabler.flake8] addopts = "--flake8" -[pytest.enabler.cov] +[tool.pytest-enabler.cov] addopts = "--cov" diff --git a/setup.cfg b/setup.cfg index 3b7ac30..baa37e5 100644 --- a/setup.cfg +++ b/setup.cfg @@ -38,7 +38,7 @@ testing = pytest-mypy >= 0.9.1; \ # workaround for jaraco/skeleton#22 python_implementation != "PyPy" - pytest-enabler >= 1.0.1 + pytest-enabler >= 1.3 # local From fea1e7cdd57d330f22ac54512ae2df19083c6ec7 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Mon, 11 Jul 2022 18:53:07 -0400 Subject: [PATCH 082/176] Ran pre-commit autoupdate --- .pre-commit-config.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index edf6f55..af50201 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,5 +1,5 @@ repos: - repo: https://github.com/psf/black - rev: 22.1.0 + rev: 22.6.0 hooks: - id: black From 325916c8240b8b3c7c41f24b664ca591e8555ea9 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Fri, 29 Jul 2022 10:12:46 -0400 Subject: [PATCH 083/176] Use '-dev' for every Python version. Ref actions/setup-python#213. --- .github/workflows/main.yml | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 948da05..de49ba8 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -7,11 +7,9 @@ jobs: strategy: matrix: python: - # Build on pre-releases until stable, then stable releases. - # actions/setup-python#213 - - ~3.7.0-0 - - ~3.10.0-0 - - ~3.11.0-0 + - 3.7 + - '3.10' + - '3.11' platform: - ubuntu-latest - macos-latest @@ -22,7 +20,7 @@ jobs: - name: Setup Python uses: actions/setup-python@v3 with: - python-version: ${{ matrix.python }} + python-version: ${{ matrix.python }}-dev - name: Install tox run: | python -m pip install tox From 424717b9e9f7c66379e809eb4e35daae827a1533 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Fri, 29 Jul 2022 10:18:19 -0400 Subject: [PATCH 084/176] Use Python 3.11 for cutting releases. --- .github/workflows/main.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index de49ba8..3ce62d9 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -52,7 +52,7 @@ jobs: - name: Setup Python uses: actions/setup-python@v3 with: - python-version: "3.10" + python-version: "3.11-dev" - name: Install tox run: | python -m pip install tox From c64902b8cafa8062398ef173278a21b042b03a77 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sat, 30 Jul 2022 19:26:15 -0400 Subject: [PATCH 085/176] Pin flake8. Workaround for tholo/pytest-flake8#87. --- setup.cfg | 2 ++ 1 file changed, 2 insertions(+) diff --git a/setup.cfg b/setup.cfg index baa37e5..1ab9350 100644 --- a/setup.cfg +++ b/setup.cfg @@ -31,6 +31,8 @@ testing = pytest >= 6 pytest-checkdocs >= 2.4 pytest-flake8 + # workaround for tholo/pytest-flake8#87 + flake8 < 5 pytest-black >= 0.3.7; \ # workaround for jaraco/skeleton#22 python_implementation != "PyPy" From 52dea7dc22e9e36771a2c84bc5efdbfa06c015df Mon Sep 17 00:00:00 2001 From: Daniel Dourvaris Date: Wed, 3 Aug 2022 09:15:15 +0300 Subject: [PATCH 086/176] Make black uniform strings --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 95477f7..22a12eb 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -3,7 +3,7 @@ requires = ["setuptools>=56", "wheel", "setuptools_scm[toml]>=3.4.1"] build-backend = "setuptools.build_meta" [tool.black] -skip-string-normalization = true +skip-string-normalization = false [tool.setuptools_scm] From abcc15683d3abe229a0e0d07f1afa05a24e2ef8c Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Thu, 18 Aug 2022 16:06:12 -0400 Subject: [PATCH 087/176] Update to setup-python v4. Fixes jaraco/skeleton#65. --- .github/workflows/main.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 3ce62d9..d17b64d 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -18,7 +18,7 @@ jobs: steps: - uses: actions/checkout@v3 - name: Setup Python - uses: actions/setup-python@v3 + uses: actions/setup-python@v4 with: python-version: ${{ matrix.python }}-dev - name: Install tox From 47c2cb324e20f784289496ef3a7b19a1cd23d196 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Thu, 18 Aug 2022 21:42:40 -0400 Subject: [PATCH 088/176] Also update release to v4 --- .github/workflows/main.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index d17b64d..63fa1e8 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -50,7 +50,7 @@ jobs: steps: - uses: actions/checkout@v3 - name: Setup Python - uses: actions/setup-python@v3 + uses: actions/setup-python@v4 with: python-version: "3.11-dev" - name: Install tox From 3d1b05aab63b13e0c1dec8db4f1a3efb631d4ac4 Mon Sep 17 00:00:00 2001 From: Pablo Prietz Date: Tue, 23 Aug 2022 09:46:12 +0200 Subject: [PATCH 089/176] pre-commit auto-update to fix black call --- .pre-commit-config.yaml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 0bb43a9..54203e4 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,6 +1,6 @@ repos: - repo: https://github.com/pre-commit/pre-commit-hooks - rev: v4.1.0 + rev: v4.3.0 hooks: - id: check-case-conflict - id: check-merge-conflict @@ -12,7 +12,7 @@ repos: - id: trailing-whitespace - repo: https://github.com/asottile/pyupgrade - rev: v2.31.1 + rev: v2.37.3 hooks: - id: pyupgrade name: PyUpgrade 3.6+ @@ -20,7 +20,7 @@ repos: exclude: ^bin/ - repo: https://github.com/pycqa/flake8 - rev: 4.0.1 + rev: 5.0.4 hooks: - id: flake8 @@ -31,11 +31,11 @@ repos: args: [--profile, black] - repo: https://github.com/psf/black - rev: 22.1.0 + rev: 22.6.0 hooks: - id: black - repo: https://github.com/asottile/setup-cfg-fmt - rev: v1.20.0 + rev: v2.0.0 hooks: - id: setup-cfg-fmt From 542b461f5c51d5785544f7737342e7633d12d9b2 Mon Sep 17 00:00:00 2001 From: Pablo Prietz Date: Tue, 23 Aug 2022 09:47:40 +0200 Subject: [PATCH 090/176] Apply new pre-commit rules --- docs/conf.py | 22 +++++++++++----------- setup.cfg | 4 ---- 2 files changed, 11 insertions(+), 15 deletions(-) diff --git a/docs/conf.py b/docs/conf.py index 7a4197d..df9c321 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -1,24 +1,24 @@ #!/usr/bin/env python3 -extensions = ['sphinx.ext.autodoc', 'jaraco.packaging.sphinx', 'rst.linker'] +extensions = ["sphinx.ext.autodoc", "jaraco.packaging.sphinx", "rst.linker"] master_doc = "index" link_files = { - '../CHANGES.rst': dict( - using=dict(GH='https://github.com'), + "../CHANGES.rst": dict( + using=dict(GH="https://github.com"), replace=[ dict( - pattern=r'(Issue #|\B#)(?P\d+)', - url='{package_url}/issues/{issue}', + pattern=r"(Issue #|\B#)(?P\d+)", + url="{package_url}/issues/{issue}", ), dict( - pattern=r'(?m:^((?Pv?\d+(\.\d+){1,2}))\n[-=]+\n)', - with_scm='{text}\n{rev[timestamp]:%d %b %Y}\n', + pattern=r"(?m:^((?Pv?\d+(\.\d+){1,2}))\n[-=]+\n)", + with_scm="{text}\n{rev[timestamp]:%d %b %Y}\n", ), dict( - pattern=r'PEP[- ](?P\d+)', - url='https://www.python.org/dev/peps/pep-{pep_number:0>4}/', + pattern=r"PEP[- ](?P\d+)", + url="https://www.python.org/dev/peps/pep-{pep_number:0>4}/", ), ], ) @@ -29,7 +29,7 @@ # Include Python intersphinx mapping to prevent failures # jaraco/skeleton#51 -extensions += ['sphinx.ext.intersphinx'] +extensions += ["sphinx.ext.intersphinx"] intersphinx_mapping = { - 'python': ('https://docs.python.org/3', None), + "python": ("https://docs.python.org/3", None), } diff --git a/setup.cfg b/setup.cfg index 63a9877..fd58a1b 100644 --- a/setup.cfg +++ b/setup.cfg @@ -14,10 +14,6 @@ classifiers = License :: OSI Approved :: MIT License Programming Language :: Python :: 3 Programming Language :: Python :: 3 :: Only - Programming Language :: Python :: 3.7 - Programming Language :: Python :: 3.8 - Programming Language :: Python :: 3.9 - Programming Language :: Python :: 3.10 [options] packages = find_namespace: From f86d791b92960e704faea60c16b1d96586bcd7ef Mon Sep 17 00:00:00 2001 From: Pablo Prietz Date: Tue, 23 Aug 2022 10:44:23 +0200 Subject: [PATCH 091/176] Only run GA on PR, tags, and manual dispatch --- .github/workflows/main.yml | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index b0606e2..6ef66e4 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -1,6 +1,11 @@ name: tests -on: [push, pull_request] +on: + pull_request: + push: + tags: + - "**" + workflow_dispatch: jobs: test: From 79ce8ba8dfd2181bce6936f78a164e3b7f539aab Mon Sep 17 00:00:00 2001 From: Pablo Prietz Date: Thu, 25 Aug 2022 21:28:30 +0200 Subject: [PATCH 092/176] Use skbuild to build and link libuvc inline --- .gitmodules | 3 ++ example.py => examples/access_100_frames.py | 0 libuvc-source | 1 + pyproject.toml | 14 +++++- pyuvc-source/CMakeLists.txt | 49 +++++++++++++++++++ controls.pxi => pyuvc-source/controls.pxi | 0 cturbojpeg.pxd => pyuvc-source/cturbojpeg.pxd | 0 cuvc.pxd => pyuvc-source/cuvc.pxd | 0 .../darwin_time.pxi | 0 linux_time.pxi => pyuvc-source/linux_time.pxi | 0 uvc.pyx => pyuvc-source/uvc.pyx | 10 ++-- pyuvc-source/uvc/__init__.py | 1 + .../windows_time.pxi | 0 scripts/build-and-test.sh | 0 setup.cfg | 35 +++++++++++++ setup.py | 29 +++++------ 16 files changed, 118 insertions(+), 24 deletions(-) create mode 100644 .gitmodules rename example.py => examples/access_100_frames.py (100%) create mode 160000 libuvc-source create mode 100644 pyuvc-source/CMakeLists.txt rename controls.pxi => pyuvc-source/controls.pxi (100%) rename cturbojpeg.pxd => pyuvc-source/cturbojpeg.pxd (100%) rename cuvc.pxd => pyuvc-source/cuvc.pxd (100%) rename darwin_time.pxi => pyuvc-source/darwin_time.pxi (100%) rename linux_time.pxi => pyuvc-source/linux_time.pxi (100%) rename uvc.pyx => pyuvc-source/uvc.pyx (98%) create mode 100644 pyuvc-source/uvc/__init__.py rename windows_time.pxi => pyuvc-source/windows_time.pxi (100%) create mode 100644 scripts/build-and-test.sh create mode 100644 setup.cfg diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..5dfc640 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "libuvc-source"] + path = libuvc-source + url = git@github.com:pupil-labs/libuvc.git diff --git a/example.py b/examples/access_100_frames.py similarity index 100% rename from example.py rename to examples/access_100_frames.py diff --git a/libuvc-source b/libuvc-source new file mode 160000 index 0000000..cd095df --- /dev/null +++ b/libuvc-source @@ -0,0 +1 @@ +Subproject commit cd095df74efe55df74d42ef91f21fb61db085e32 diff --git a/pyproject.toml b/pyproject.toml index 8f9f7ea..6fe2b5d 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,3 +1,15 @@ [build-system] -requires = ["setuptools", "wheel", "Cython", "numpy", "pkgconfig"] +requires = [ + "setuptools", + "setuptools_scm[toml]>=3.4.1", + "wheel", + "Cython", + "numpy", + "pkgconfig", + "scikit-build", + "cmake", + "ninja;platform_system != \"Windows\"", +] build-backend = "setuptools.build_meta" + +[tool.setuptools_scm] diff --git a/pyuvc-source/CMakeLists.txt b/pyuvc-source/CMakeLists.txt new file mode 100644 index 0000000..b10ca08 --- /dev/null +++ b/pyuvc-source/CMakeLists.txt @@ -0,0 +1,49 @@ +project(pyuvc) +cmake_minimum_required(VERSION 3.3) + +add_subdirectory(../libuvc-source skbuild-libuvc) + +# find skbuild cmake packages +find_package(PythonExtensions REQUIRED) +find_package(Cython REQUIRED) + +# find external cmake packages +find_package(NumPy REQUIRED) + +find_package(PkgConfig) +pkg_check_modules(libturbojpeg REQUIRED libturbojpeg) +pkg_check_modules(LIBUSB REQUIRED libusb-1.0) +pkg_check_modules(libuvc REQUIRED libuvc) + +# include cpp folder for finding headers +include_directories(".") + +# include numpy headers +include_directories(${NumPy_INCLUDE_DIRS}) +include_directories(${libuvc_INCLUDE_DIRS}) +include_directories(${libturbojpeg_INCLUDE_DIRS}) +include_directories(${LIBUSB_INCLUDE_DIRS}) + +link_directories(${libuvc_LIBRARY_DIRS} ${libturbojpeg_LIBRARY_DIRS}) + +# Note: add_cython_target does not actually add a target, but fills a variable with the +# corresponding compiled source file, e.g. here 'pupil_detection_3d.cxx'. If only the +# name is specified, it will look for a cython file with the same base name: +# 'pupil_detection_3d.pyx' in this case. +add_cython_target(uvc CXX PY3) + +# Create a module library from the source file and wrap it with settings for +# creating a python extension. +add_library(uvc MODULE ${uvc}) +python_extension_module(uvc) + +# libuvc_LIBRARIES includes `uvc` which cannot be passed to `target_link_libraries` +# as it will attempt to link to itself and fail. By prepending `-l`, we avoid the +# ambiguity +list(TRANSFORM libuvc_LIBRARIES PREPEND -l) + +# link against external libraries +target_link_libraries(uvc ${libuvc_LIBRARIES} ${libturbojpeg_LIBRARIES}) + +# install here +install(TARGETS uvc LIBRARY DESTINATION ".") \ No newline at end of file diff --git a/controls.pxi b/pyuvc-source/controls.pxi similarity index 100% rename from controls.pxi rename to pyuvc-source/controls.pxi diff --git a/cturbojpeg.pxd b/pyuvc-source/cturbojpeg.pxd similarity index 100% rename from cturbojpeg.pxd rename to pyuvc-source/cturbojpeg.pxd diff --git a/cuvc.pxd b/pyuvc-source/cuvc.pxd similarity index 100% rename from cuvc.pxd rename to pyuvc-source/cuvc.pxd diff --git a/darwin_time.pxi b/pyuvc-source/darwin_time.pxi similarity index 100% rename from darwin_time.pxi rename to pyuvc-source/darwin_time.pxi diff --git a/linux_time.pxi b/pyuvc-source/linux_time.pxi similarity index 100% rename from linux_time.pxi rename to pyuvc-source/linux_time.pxi diff --git a/uvc.pyx b/pyuvc-source/uvc.pyx similarity index 98% rename from uvc.pyx rename to pyuvc-source/uvc.pyx index 279400b..120799a 100644 --- a/uvc.pyx +++ b/pyuvc-source/uvc.pyx @@ -652,10 +652,10 @@ cdef class Capture: cdef _enumerate_controls(self): - cdef uvc.uvc_input_terminal_t *input_terminal = uvc.uvc_get_input_terminals(self.devh) - cdef uvc.uvc_output_terminal_t *output_terminal = uvc.uvc_get_output_terminals(self.devh) - cdef uvc.uvc_processing_unit_t *processing_unit = uvc.uvc_get_processing_units(self.devh) - cdef uvc.uvc_extension_unit_t *extension_unit = uvc.uvc_get_extension_units(self.devh) + cdef uvc.uvc_input_terminal_t *input_terminal = uvc.uvc_get_input_terminals(self.devh) + cdef uvc.uvc_output_terminal_t *output_terminal = uvc.uvc_get_output_terminals(self.devh) + cdef uvc.uvc_processing_unit_t *processing_unit = uvc.uvc_get_processing_units(self.devh) + cdef uvc.uvc_extension_unit_t *extension_unit = uvc.uvc_get_extension_units(self.devh) cdef int x = 0 avaible_controls_per_unit = {} @@ -698,7 +698,7 @@ cdef class Capture: cdef _enumerate_formats(self): - cdef uvc.uvc_format_desc_t *format_desc = uvc.uvc_get_format_descs(self.devh) + cdef uvc.uvc_format_desc_t *format_desc = uvc.uvc_get_format_descs(self.devh) cdef uvc.uvc_frame_desc *frame_desc cdef int i self._available_modes = [] diff --git a/pyuvc-source/uvc/__init__.py b/pyuvc-source/uvc/__init__.py new file mode 100644 index 0000000..cd9c93b --- /dev/null +++ b/pyuvc-source/uvc/__init__.py @@ -0,0 +1 @@ +from .uvc import * diff --git a/windows_time.pxi b/pyuvc-source/windows_time.pxi similarity index 100% rename from windows_time.pxi rename to pyuvc-source/windows_time.pxi diff --git a/scripts/build-and-test.sh b/scripts/build-and-test.sh new file mode 100644 index 0000000..e69de29 diff --git a/setup.cfg b/setup.cfg new file mode 100644 index 0000000..0432529 --- /dev/null +++ b/setup.cfg @@ -0,0 +1,35 @@ +[metadata] +name = uvc +description = Usb Video Class Device bindings with format conversion tool +long_description = file: README.md +long_description_content_type = text/x-markdown +url = https://github.com/pupil-labs/pyuvc +author = Pupil Labs GmbH +author_email = info@pupil-labs.com +license = MIT +license_file = LICENSE +classifiers = + Development Status :: 5 - Production/Stable + Intended Audience :: Developers + License :: OSI Approved :: MIT License + Programming Language :: Python :: 3 + Programming Language :: Python :: 3 :: Only + +[options] +install_requires = + numpy + importlib-metadata;python_version<"3.8" +python_requires = >=3.6 + +[options.extras_require] +docs = + furo + jaraco.packaging>=9 + rst.linker>=1.9 + sphinx +testing = + pytest>=6 + pytest-checkdocs>=2.4 + pytest-cov + pytest-enabler>=1.3 + pytest-mypy>=0.9.1;python_implementation != "PyPy" diff --git a/setup.py b/setup.py index 7ba3723..06749ec 100644 --- a/setup.py +++ b/setup.py @@ -1,20 +1,10 @@ -""" -(*)~---------------------------------------------------------------------------------- - Pupil - eye tracking platform - Copyright (C) 2012-2015 Pupil Labs - - Distributed under the terms of the CC BY-NC-SA License. - License details are in the file license.txt, distributed as part of this software. -----------------------------------------------------------------------------------~(*) -""" -import glob -import os import platform import numpy import pkgconfig from Cython.Build import cythonize -from setuptools import Extension, setup +from setuptools import Extension, find_packages +from skbuild import setup extra_link_args = [] plat_data_files = [] @@ -73,11 +63,14 @@ ) ] +pyuvc_source_folder = "pyuvc-source" + setup( - name="uvc", - version="0.16.0", - description="Usb Video Class Device bindings with format conversion tool.", - install_requires=["numpy"], - ext_modules=cythonize(extensions), - data_files=plat_data_files, + # ext_modules=cythonize(extensions), + # data_files=plat_data_files, + packages=find_packages(where=pyuvc_source_folder), + package_dir={"": pyuvc_source_folder}, + include_package_data=False, + cmake_source_dir=pyuvc_source_folder, + cmake_install_dir=pyuvc_source_folder + "/uvc", ) From a3a39828d9ace698546c8da4b162f27c0d5b3e3b Mon Sep 17 00:00:00 2001 From: Pablo Prietz Date: Sat, 27 Aug 2022 09:39:26 +0200 Subject: [PATCH 093/176] Remove outdated setup.py code --- setup.py | 66 +------------------------------------------------------- 1 file changed, 1 insertion(+), 65 deletions(-) diff --git a/setup.py b/setup.py index 06749ec..2743ec0 100644 --- a/setup.py +++ b/setup.py @@ -1,73 +1,9 @@ -import platform - -import numpy -import pkgconfig -from Cython.Build import cythonize -from setuptools import Extension, find_packages +from setuptools import find_packages from skbuild import setup -extra_link_args = [] -plat_data_files = [] -extra_objects = [] -library_dirs = [] -include_dirs = [numpy.get_include()] -if platform.system() == "Darwin": - configs = pkgconfig.parse("libturbojpeg libuvc") - libs = configs["libraries"] - include_dirs += configs["include_dirs"] - library_dirs += configs["library_dirs"] -elif platform.system() == "Linux": - libs = ["rt", "uvc", "turbojpeg"] -elif platform.system() == "Windows": - pack_dir = "" - uvc_dir = "C:\\work\\libuvc" - tj_dir = "C:\\work\\libjpeg-turbo-VC64" - usb_dir = "C:\\work\\libusb" - pthread_dir = "C:\\work\\pthreads-w32-2-9-1-release\\Pre-built.2\\dll\\x64" - - tj_lib = tj_dir + "\\lib\\turbojpeg.lib" - uvc_lib = uvc_dir + "\\bin\\Release\\uvc.lib" - - uvc_dll = uvc_dir + "\\bin\\Release\\uvc.dll" - usb_dll = usb_dir + "\\x64\Release\\dll\\libusb-1.0.dll" - tj_dll = tj_dir + "\\bin\\turbojpeg.dll" - jpg_dll = tj_dir + "\\bin\\jpeg62.dll" - pthr_dll = pthread_dir + "\\pthreadVC2.dll" - - extra_objects = [tj_lib, uvc_lib] - libs = ["winmm"] - extra_link_args = [] - include_dirs += [tj_dir + "\\include"] - include_dirs += [usb_dir] + [usb_dir + "\\libusb"] - include_dirs += [uvc_dir + "\\include"] + [uvc_dir + "\\bin\\include"] - - plat_data_files = [ - (pack_dir, [uvc_dll]), - (pack_dir, [usb_dll]), - (pack_dir, [tj_dll]), - (pack_dir, [jpg_dll]), - (pack_dir, [pthr_dll]), - ] - - -extensions = [ - Extension( - name="uvc", - sources=["uvc.pyx"], - include_dirs=include_dirs, - library_dirs=library_dirs, - libraries=libs, - extra_link_args=extra_link_args, - extra_objects=extra_objects, - extra_compile_args=[], - ) -] - pyuvc_source_folder = "pyuvc-source" setup( - # ext_modules=cythonize(extensions), - # data_files=plat_data_files, packages=find_packages(where=pyuvc_source_folder), package_dir={"": pyuvc_source_folder}, include_package_data=False, From 68007b77b1fe597a624def9cf0f5bec45b05c153 Mon Sep 17 00:00:00 2001 From: Pablo Prietz Date: Sat, 27 Aug 2022 11:29:43 +0200 Subject: [PATCH 094/176] Extend .gitignore --- .gitignore | 315 ++++++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 300 insertions(+), 15 deletions(-) diff --git a/.gitignore b/.gitignore index dab2dd5..59f4983 100644 --- a/.gitignore +++ b/.gitignore @@ -1,30 +1,192 @@ +version.py + +# Created by https://www.toptal.com/developers/gitignore/api/python,visualstudiocode,macos,windows,linux,pycharm +# Edit at https://www.toptal.com/developers/gitignore?templates=python,visualstudiocode,macos,windows,linux,pycharm + +### Linux ### +*~ + +# temporary files which can be created if a process still has a handle open of a deleted file +.fuse_hidden* + +# KDE directory preferences +.directory + +# Linux trash folder which might appear on any partition or disk +.Trash-* + +# .nfs files are created when an open file is removed but is still being accessed +.nfs* + +### macOS ### +# General +.DS_Store +.AppleDouble +.LSOverride + +# Icon must end with two \r +Icon + + +# Thumbnails +._* + +# Files that might appear in the root of a volume +.DocumentRevisions-V100 +.fseventsd +.Spotlight-V100 +.TemporaryItems +.Trashes +.VolumeIcon.icns +.com.apple.timemachine.donotpresent + +# Directories potentially created on remote AFP share +.AppleDB +.AppleDesktop +Network Trash Folder +Temporary Items +.apdisk + +### PyCharm ### +# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider +# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 + +# User-specific stuff +.idea/**/workspace.xml +.idea/**/tasks.xml +.idea/**/usage.statistics.xml +.idea/**/dictionaries +.idea/**/shelf + +# AWS User-specific +.idea/**/aws.xml + +# Generated files +.idea/**/contentModel.xml + +# Sensitive or high-churn files +.idea/**/dataSources/ +.idea/**/dataSources.ids +.idea/**/dataSources.local.xml +.idea/**/sqlDataSources.xml +.idea/**/dynamic.xml +.idea/**/uiDesigner.xml +.idea/**/dbnavigator.xml + +# Gradle +.idea/**/gradle.xml +.idea/**/libraries + +# Gradle and Maven with auto-import +# When using Gradle or Maven with auto-import, you should exclude module files, +# since they will be recreated, and may cause churn. Uncomment if using +# auto-import. +# .idea/artifacts +# .idea/compiler.xml +# .idea/jarRepositories.xml +# .idea/modules.xml +# .idea/*.iml +# .idea/modules +# *.iml +# *.ipr + +# CMake +cmake-build-*/ + +# Mongo Explorer plugin +.idea/**/mongoSettings.xml + +# File-based project format +*.iws + +# IntelliJ +out/ + +# mpeltonen/sbt-idea plugin +.idea_modules/ + +# JIRA plugin +atlassian-ide-plugin.xml + +# Cursive Clojure plugin +.idea/replstate.xml + +# Crashlytics plugin (for Android Studio and IntelliJ) +com_crashlytics_export_strings.xml +crashlytics.properties +crashlytics-build.properties +fabric.properties + +# Editor-based Rest Client +.idea/httpRequests + +# Android studio 3.1+ serialized cache file +.idea/caches/build_file_checksums.ser + +### PyCharm Patch ### +# Comment Reason: https://github.com/joeblau/gitignore.io/issues/186#issuecomment-215987721 + +# *.iml +# modules.xml +# .idea/misc.xml +# *.ipr + +# Sonarlint plugin +# https://plugins.jetbrains.com/plugin/7973-sonarlint +.idea/**/sonarlint/ + +# SonarQube Plugin +# https://plugins.jetbrains.com/plugin/7238-sonarqube-community-plugin +.idea/**/sonarIssues.xml + +# Markdown Navigator plugin +# https://plugins.jetbrains.com/plugin/7896-markdown-navigator-enhanced +.idea/**/markdown-navigator.xml +.idea/**/markdown-navigator-enh.xml +.idea/**/markdown-navigator/ + +# Cache file creation bug +# See https://youtrack.jetbrains.com/issue/JBR-2257 +.idea/$CACHE_FILE$ + +# CodeStream plugin +# https://plugins.jetbrains.com/plugin/12206-codestream +.idea/codestream.xml + +### Python ### # Byte-compiled / optimized / DLL files __pycache__/ *.py[cod] - -# C source -*.c +*$py.class # C extensions *.so - # Distribution / packaging .Python -env/ -bin/ build/ develop-eggs/ dist/ +downloads/ eggs/ +.eggs/ lib/ lib64/ parts/ sdist/ var/ +wheels/ +share/python-wheels/ *.egg-info/ .installed.cfg *.egg +MANIFEST + +# PyInstaller +# Usually these files are written by a python script from a template +# before PyInstaller builds the exe, so as to inject date/other infos into it. +*.manifest +*.spec # Installer logs pip-log.txt @@ -33,26 +195,149 @@ pip-delete-this-directory.txt # Unit test / coverage reports htmlcov/ .tox/ +.nox/ .coverage +.coverage.* .cache nosetests.xml coverage.xml +*.cover +*.py,cover +.hypothesis/ +.pytest_cache/ +cover/ # Translations *.mo - -# Mr Developer -.mr.developer.cfg -.project -.pydevproject - -# Rope -.ropeproject +*.pot # Django stuff: *.log -*.pot +local_settings.py +db.sqlite3 +db.sqlite3-journal + +# Flask stuff: +instance/ +.webassets-cache + +# Scrapy stuff: +.scrapy # Sphinx documentation docs/_build/ +# PyBuilder +.pybuilder/ +target/ + +# Jupyter Notebook +.ipynb_checkpoints + +# IPython +profile_default/ +ipython_config.py + +# pyenv +# For a library or package, you might want to ignore these files since the code is +# intended to run in multiple environments; otherwise, check them in: +# .python-version + +# pipenv +# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. +# However, in case of collaboration, if having platform-specific dependencies or dependencies +# having no cross-platform support, pipenv may install dependencies that don't work, or not +# install all needed dependencies. +#Pipfile.lock + +# PEP 582; used by e.g. github.com/David-OConnor/pyflow +__pypackages__/ + +# Celery stuff +celerybeat-schedule +celerybeat.pid + +# SageMath parsed files +*.sage.py + +# Environments +.env +.venv +env/ +venv/ +ENV/ +env.bak/ +venv.bak/ + +# Spyder project settings +.spyderproject +.spyproject + +# Rope project settings +.ropeproject + +# mkdocs documentation +/site + +# mypy +.mypy_cache/ +.dmypy.json +dmypy.json + +# Pyre type checker +.pyre/ + +# pytype static type analyzer +.pytype/ + +# Cython debug symbols +cython_debug/ + +### VisualStudioCode ### +.vscode/* +!.vscode/settings.json +!.vscode/tasks.json +!.vscode/launch.json +!.vscode/extensions.json +*.code-workspace + +# Local History for Visual Studio Code +.history/ + +### VisualStudioCode Patch ### +# Ignore all local history of files +.history +.ionide + +# Support for Project snippet scope +!.vscode/*.code-snippets + +### Windows ### +# Windows thumbnail cache files +Thumbs.db +Thumbs.db:encryptable +ehthumbs.db +ehthumbs_vista.db + +# Dump file +*.stackdump + +# Folder config file +[Dd]esktop.ini + +# Recycle Bin used on file shares +$RECYCLE.BIN/ + +# Windows Installer files +*.cab +*.msi +*.msix +*.msm +*.msp + +# Windows shortcuts +*.lnk + +# End of https://www.toptal.com/developers/gitignore/api/python,visualstudiocode,macos,windows,linux,pycharm + +_skbuild/ \ No newline at end of file From 4383857f97a682dbea9c597acd80797e3ca8bb5c Mon Sep 17 00:00:00 2001 From: Pablo Prietz Date: Sat, 27 Aug 2022 11:29:58 +0200 Subject: [PATCH 095/176] Remove .bumpversion.cfg --- .bumpversion.cfg | 8 -------- 1 file changed, 8 deletions(-) delete mode 100644 .bumpversion.cfg diff --git a/.bumpversion.cfg b/.bumpversion.cfg deleted file mode 100644 index 1fdd581..0000000 --- a/.bumpversion.cfg +++ /dev/null @@ -1,8 +0,0 @@ -[bumpversion] -current_version = 0.15.0 -commit = True -tag = True - -[bumpversion:file:setup.py] - -[bumpversion:file:uvc.pyx] From 0f6d8d36f0d94b3c8b44610df8366934f676f74c Mon Sep 17 00:00:00 2001 From: Pablo Prietz Date: Sun, 28 Aug 2022 11:51:53 +0200 Subject: [PATCH 096/176] Use modern cmake --- MANIFEST.in | 9 +++- libuvc-source | 2 +- pyuvc-source/CMakeLists.txt | 49 ++++++++++------------ pyuvc-source/uvc/__init__.py | 2 +- pyuvc-source/{uvc.pyx => uvc_bindings.pyx} | 0 5 files changed, 33 insertions(+), 29 deletions(-) rename pyuvc-source/{uvc.pyx => uvc_bindings.pyx} (100%) diff --git a/MANIFEST.in b/MANIFEST.in index 9f122b8..9cf64c5 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -1 +1,8 @@ -recursive-include . *.pyx *.pxd *.pxi *.h *.c *.cpp +recursive-include libuvc-source *.h *.c CMakeLists.txt Config.cmake.in +recursive-include pyuvc-source *.h *.pyx CMakeLists.txt + +global-exclude *.dll *.so *.dylib + +global-exclude .DS_Store +prune ./.*/ +prune **/__pycache__ \ No newline at end of file diff --git a/libuvc-source b/libuvc-source index cd095df..f995462 160000 --- a/libuvc-source +++ b/libuvc-source @@ -1 +1 @@ -Subproject commit cd095df74efe55df74d42ef91f21fb61db085e32 +Subproject commit f99546223b452caad66ad829b5e9092855044ec6 diff --git a/pyuvc-source/CMakeLists.txt b/pyuvc-source/CMakeLists.txt index b10ca08..63e70b1 100644 --- a/pyuvc-source/CMakeLists.txt +++ b/pyuvc-source/CMakeLists.txt @@ -1,5 +1,9 @@ -project(pyuvc) -cmake_minimum_required(VERSION 3.3) +cmake_minimum_required(VERSION 3.24) +project(pyuvc + LANGUAGES C + DESCRIPTION "Python bindings for the Pupil Labs libuvc library" + HOMEPAGE_URL "https://github.com/pupil-labs/pyuvc" +) add_subdirectory(../libuvc-source skbuild-libuvc) @@ -13,37 +17,30 @@ find_package(NumPy REQUIRED) find_package(PkgConfig) pkg_check_modules(libturbojpeg REQUIRED libturbojpeg) pkg_check_modules(LIBUSB REQUIRED libusb-1.0) -pkg_check_modules(libuvc REQUIRED libuvc) - -# include cpp folder for finding headers -include_directories(".") - -# include numpy headers -include_directories(${NumPy_INCLUDE_DIRS}) -include_directories(${libuvc_INCLUDE_DIRS}) -include_directories(${libturbojpeg_INCLUDE_DIRS}) -include_directories(${LIBUSB_INCLUDE_DIRS}) - -link_directories(${libuvc_LIBRARY_DIRS} ${libturbojpeg_LIBRARY_DIRS}) # Note: add_cython_target does not actually add a target, but fills a variable with the -# corresponding compiled source file, e.g. here 'pupil_detection_3d.cxx'. If only the +# corresponding compiled source file, e.g. here 'uvc_bindings.c'. If only the # name is specified, it will look for a cython file with the same base name: -# 'pupil_detection_3d.pyx' in this case. -add_cython_target(uvc CXX PY3) +# 'uvc_bindings.pyx' in this case. +set(CYTHON_ANNOTATE TRUE) +add_cython_target(uvc_bindings C PY3) # CXX does not generate a corretc module # Create a module library from the source file and wrap it with settings for # creating a python extension. -add_library(uvc MODULE ${uvc}) -python_extension_module(uvc) +add_library(uvc_bindings MODULE ${uvc_bindings}) +python_extension_module(uvc_bindings) + +# include cpp folder for finding headers +target_include_directories(uvc_bindings PRIVATE ".") + +# include numpy headers +target_include_directories(uvc_bindings PRIVATE ${NumPy_INCLUDE_DIRS}) +target_include_directories(uvc_bindings PRIVATE ${libturbojpeg_INCLUDE_DIRS}) +target_include_directories(uvc_bindings PRIVATE ${LIBUSB_INCLUDE_DIRS}) -# libuvc_LIBRARIES includes `uvc` which cannot be passed to `target_link_libraries` -# as it will attempt to link to itself and fail. By prepending `-l`, we avoid the -# ambiguity -list(TRANSFORM libuvc_LIBRARIES PREPEND -l) +target_link_directories(uvc_bindings PRIVATE ${libturbojpeg_LIBRARY_DIRS}) -# link against external libraries -target_link_libraries(uvc ${libuvc_LIBRARIES} ${libturbojpeg_LIBRARIES}) +target_link_libraries(uvc_bindings PLLibUVC::uvc ${libturbojpeg_LIBRARIES}) # install here -install(TARGETS uvc LIBRARY DESTINATION ".") \ No newline at end of file +install(TARGETS uvc_bindings LIBRARY DESTINATION ".") \ No newline at end of file diff --git a/pyuvc-source/uvc/__init__.py b/pyuvc-source/uvc/__init__.py index cd9c93b..492b78a 100644 --- a/pyuvc-source/uvc/__init__.py +++ b/pyuvc-source/uvc/__init__.py @@ -1 +1 @@ -from .uvc import * +from .uvc_bindings import * diff --git a/pyuvc-source/uvc.pyx b/pyuvc-source/uvc_bindings.pyx similarity index 100% rename from pyuvc-source/uvc.pyx rename to pyuvc-source/uvc_bindings.pyx From fd48b27104bf39850fd8fecb8eab82c5b7c1f7b3 Mon Sep 17 00:00:00 2001 From: Pablo Prietz Date: Mon, 29 Aug 2022 08:31:10 +0200 Subject: [PATCH 097/176] Build and test script --- scripts/build-and-test.sh | 15 +++++++++++++++ 1 file changed, 15 insertions(+) mode change 100644 => 100755 scripts/build-and-test.sh diff --git a/scripts/build-and-test.sh b/scripts/build-and-test.sh old mode 100644 new mode 100755 index e69de29..06b97f2 --- a/scripts/build-and-test.sh +++ b/scripts/build-and-test.sh @@ -0,0 +1,15 @@ +#!/bin/bash -xe +git clean -dxf -e .venv/ +export PKG_CONFIG_PATH=/opt/homebrew/opt/jpeg-turbo/lib/pkgconfig + +BUILD_WHEEL=true +if ["$BUILD_WHEEL" = true] +then + py -m build . --wheel + delocate-wheel -v dist/*.whl + pip install dist/*.whl --force-reinstall +else + py -m build . --sdist -n + pip install dist/*.tar.gz --force-reinstall +fi +python examples/access_100_frames.py \ No newline at end of file From e9b9abe2c77b41941abac4a83c5812271591e327 Mon Sep 17 00:00:00 2001 From: Pablo Prietz Date: Mon, 29 Aug 2022 13:55:01 +0200 Subject: [PATCH 098/176] Fix sdist builds by correctly setting up rpath --- libuvc-source | 2 +- pyuvc-source/CMakeLists.txt | 1 + scripts/build-and-test.sh | 6 +++--- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/libuvc-source b/libuvc-source index f995462..0f9d0d0 160000 --- a/libuvc-source +++ b/libuvc-source @@ -1 +1 @@ -Subproject commit f99546223b452caad66ad829b5e9092855044ec6 +Subproject commit 0f9d0d0ebdddae8166a77930dd61c5b877198cac diff --git a/pyuvc-source/CMakeLists.txt b/pyuvc-source/CMakeLists.txt index 63e70b1..ac499db 100644 --- a/pyuvc-source/CMakeLists.txt +++ b/pyuvc-source/CMakeLists.txt @@ -6,6 +6,7 @@ project(pyuvc ) add_subdirectory(../libuvc-source skbuild-libuvc) +set(CMAKE_INSTALL_RPATH @loader_path/${CMAKE_INSTALL_LIBDIR}) # find skbuild cmake packages find_package(PythonExtensions REQUIRED) diff --git a/scripts/build-and-test.sh b/scripts/build-and-test.sh index 06b97f2..2c22e02 100755 --- a/scripts/build-and-test.sh +++ b/scripts/build-and-test.sh @@ -3,13 +3,13 @@ git clean -dxf -e .venv/ export PKG_CONFIG_PATH=/opt/homebrew/opt/jpeg-turbo/lib/pkgconfig BUILD_WHEEL=true -if ["$BUILD_WHEEL" = true] +if [ "$BUILD_WHEEL" = true ] then py -m build . --wheel delocate-wheel -v dist/*.whl - pip install dist/*.whl --force-reinstall + pip install -vv dist/*.whl --force-reinstall else py -m build . --sdist -n - pip install dist/*.tar.gz --force-reinstall + pip install -vv dist/*.tar.gz --force-reinstall fi python examples/access_100_frames.py \ No newline at end of file From 57b5fb495eb1e7a45616590f8bd7579fa2814a64 Mon Sep 17 00:00:00 2001 From: Pablo Prietz Date: Wed, 31 Aug 2022 17:31:35 +0200 Subject: [PATCH 099/176] Update libuvc --- libuvc-source | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libuvc-source b/libuvc-source index 0f9d0d0..b1f0b24 160000 --- a/libuvc-source +++ b/libuvc-source @@ -1 +1 @@ -Subproject commit 0f9d0d0ebdddae8166a77930dd61c5b877198cac +Subproject commit b1f0b24c606ff2deb9fd0d56339283e89fe4da28 From 0e40b27e78df9b7e249f3d748cb0cde137a8882c Mon Sep 17 00:00:00 2001 From: Pablo Prietz Date: Wed, 31 Aug 2022 17:31:53 +0200 Subject: [PATCH 100/176] Fix access_100_frames example --- examples/access_100_frames.py | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/examples/access_100_frames.py b/examples/access_100_frames.py index 053e937..7ec7866 100644 --- a/examples/access_100_frames.py +++ b/examples/access_100_frames.py @@ -14,12 +14,8 @@ # controls_dict['Gamma'].value = 200 print(cap.avaible_modes) -for x in range(10): - print(x) - cap.frame_mode = (640, 480, 30) - for x in range(100): - frame = cap.get_frame_robust() - print(frame.img.shape) - # cv2.imshow("img",frame.gray) - # cv2.waitKey(1) +cap.frame_mode = cap.avaible_modes[0] +for x in range(100): + frame = cap.get_frame_robust() + print(frame.img.mean()) cap = None From 8d27e5d30d9e89834ec8daf09c06ccded0a13c50 Mon Sep 17 00:00:00 2001 From: Pablo Prietz Date: Thu, 1 Sep 2022 08:43:24 +0200 Subject: [PATCH 101/176] Update libuvc --- libuvc-source | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libuvc-source b/libuvc-source index b1f0b24..d4a2181 160000 --- a/libuvc-source +++ b/libuvc-source @@ -1 +1 @@ -Subproject commit b1f0b24c606ff2deb9fd0d56339283e89fe4da28 +Subproject commit d4a2181b811a9df4544da24a92237fb7cafffae9 From f41f15b0f4105888ffcda42a6968ff23ffa91f85 Mon Sep 17 00:00:00 2001 From: Pablo Prietz Date: Mon, 5 Sep 2022 09:13:08 +0200 Subject: [PATCH 102/176] Update libuvc --- libuvc-source | 2 +- pyuvc-source/uvc_bindings.pyx | 2 -- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/libuvc-source b/libuvc-source index d4a2181..1dba491 160000 --- a/libuvc-source +++ b/libuvc-source @@ -1 +1 @@ -Subproject commit d4a2181b811a9df4544da24a92237fb7cafffae9 +Subproject commit 1dba4913271d94e5cf851379c4f0d41872a60091 diff --git a/pyuvc-source/uvc_bindings.pyx b/pyuvc-source/uvc_bindings.pyx index 120799a..d095ebd 100644 --- a/pyuvc-source/uvc_bindings.pyx +++ b/pyuvc-source/uvc_bindings.pyx @@ -623,8 +623,6 @@ cdef class Capture: return frame raise StreamError("Could not grab frame after 3 attempts. Giving up.") - - def get_frame(self,timeout=0): cdef int status, j_width,j_height,jpegSubsamp,header_ok cdef int timeout_usec = int(timeout*1e6) #sec to usec From a688e4e77ff275787058e4292f2dc41e29edcdc1 Mon Sep 17 00:00:00 2001 From: Pablo Prietz Date: Mon, 5 Sep 2022 10:31:39 +0200 Subject: [PATCH 103/176] Define pyuvc cmake source dir for VS Code --- .vscode/settings.json | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 .vscode/settings.json diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..55ab0d5 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,3 @@ +{ + "cmake.sourceDirectory": "${workspaceFolder}/pyuvc-source", +} \ No newline at end of file From 0f3b38d94c570a6c80970ffce0855471b9fee97d Mon Sep 17 00:00:00 2001 From: Pablo Prietz Date: Mon, 5 Sep 2022 10:32:22 +0200 Subject: [PATCH 104/176] Define logger in top-level module for backwards consistency --- pyuvc-source/uvc/__init__.py | 5 +++++ pyuvc-source/uvc_bindings.pyx | 3 +-- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/pyuvc-source/uvc/__init__.py b/pyuvc-source/uvc/__init__.py index 492b78a..7d271b8 100644 --- a/pyuvc-source/uvc/__init__.py +++ b/pyuvc-source/uvc/__init__.py @@ -1 +1,6 @@ +import logging + + +logger = logging.getLogger(__name__) + from .uvc_bindings import * diff --git a/pyuvc-source/uvc_bindings.pyx b/pyuvc-source/uvc_bindings.pyx index d095ebd..cdfb5d8 100644 --- a/pyuvc-source/uvc_bindings.pyx +++ b/pyuvc-source/uvc_bindings.pyx @@ -28,8 +28,7 @@ ELIF UNAME_SYSNAME == "Darwin": ELIF UNAME_SYSNAME == "Linux": include "linux_time.pxi" - -logger = logging.getLogger(__name__) +from . import logger # Until version 1.0.24, libusb did not support detaching kernel drivers on macOS. As a # result, the cameras could only be accessed if no other driver was attached. In macOS From 1787611a91755e3d67f77167b26169789ff023f4 Mon Sep 17 00:00:00 2001 From: Pablo Prietz Date: Mon, 5 Sep 2022 11:16:51 +0200 Subject: [PATCH 105/176] Clean up error messages --- pyuvc-source/uvc_bindings.pyx | 95 +++++++++++++++++------------------ 1 file changed, 46 insertions(+), 49 deletions(-) diff --git a/pyuvc-source/uvc_bindings.pyx b/pyuvc-source/uvc_bindings.pyx index cdfb5d8..7cfa15f 100644 --- a/pyuvc-source/uvc_bindings.pyx +++ b/pyuvc-source/uvc_bindings.pyx @@ -63,23 +63,25 @@ else: IS_MACOS_BIG_SUR_OR_OLDER = False cdef int SHOULD_DETACH_KERNEL_DRIVER = int(not IS_MACOS_BIG_SUR_OR_OLDER) -uvc_error_codes = { 0:"Success (no error)", - -1:"Input/output error.", - -2:"Invalid parameter.", - -3:"Access denied.", - -4:"No such device.", - -5:"Entity not found.", - -6:"Resource busy.", - -7:"Operation timed out.", - -8:"Overflow.", - -9:"Pipe error.", - -10:"System call interrupted.", - -11:"Insufficient memory. ", - -12:"Operation not supported.", - -50:"Device is not UVC-compliant.", - -51:"Mode not supported.", - -52:"Resource has a callback (can't use polling and async)", - -99:"Undefined error."} +uvc_error_codes = { + 0:"Success (no error)", + -1:"Input/output error", + -2:"Invalid parameter", + -3:"Access denied", + -4:"No such device", + -5:"Entity not found", + -6:"Resource busy", + -7:"Operation timed out", + -8:"Overflow", + -9:"Pipe error", + -10:"System call interrupted", + -11:"Insufficient memory", + -12:"Operation not supported", + -50:"Device is not UVC-compliant", + -51:"Mode not supported", + -52:"Resource has a callback (can't use polling and async)", + -99:"Undefined error", +} cdef str _to_str(object s): @@ -89,29 +91,20 @@ cdef str _to_str(object s): return (s).decode('utf-8') class CaptureError(Exception): - def __init__(self, message): - super(CaptureError, self).__init__() - self.message = message + pass class StreamError(CaptureError): - def __init__(self, message): - super(StreamError, self).__init__(message) - self.message = message + pass class InitError(CaptureError): - def __init__(self, message): - super(InitError, self).__init__(message) - self.message = message + pass class OpenError(InitError): - def __init__(self, message): - super(InitError, self).__init__(message) - self.message = message + pass class DeviceNotFoundError(InitError): def __init__(self, message): - super(InitError, self).__init__(message) - self.message = message + super().__init__(message) __version__ = '0.14' #make sure this is the same in setup.py @@ -510,38 +503,42 @@ cdef class Capture: break device_address = uvc.uvc_get_device_address(dev) bus_number = uvc.uvc_get_bus_number(dev) - if dev_uid == '%s:%s'%(bus_number,device_address): - logger.debug("Found device that mached uid:'%s'"%dev_uid) + dev_uid_found = f'{bus_number}:{device_address}' + if dev_uid == dev_uid_found: + logger.debug(f"Found device that mached uid: {dev_uid}") uvc.uvc_ref_device(dev) if (uvc.uvc_get_device_descriptor(dev, &desc) == uvc.UVC_SUCCESS): - product = desc.product or "unknown" - manufacturer = desc.manufacturer or "unknown" - serialNumber = desc.serialNumber or "unknown" - idProduct,idVendor = desc.idProduct,desc.idVendor - device_address = uvc.uvc_get_device_address(dev) - bus_number = uvc.uvc_get_bus_number(dev) - self._info = {'name':_to_str(product), - 'manufacturer':_to_str(manufacturer), - 'serialNumber':_to_str(serialNumber), - 'idProduct':idProduct, - 'idVendor':idVendor, - 'device_address':device_address, - 'bus_number':bus_number, - 'uid':'%s:%s'%(bus_number,device_address)} + product = desc.product or "unknown" + manufacturer = desc.manufacturer or "unknown" + serialNumber = desc.serialNumber or "unknown" + idProduct,idVendor = desc.idProduct,desc.idVendor + device_address = uvc.uvc_get_device_address(dev) + bus_number = uvc.uvc_get_bus_number(dev) + self._info = { + 'name': _to_str(product), + 'manufacturer': _to_str(manufacturer), + 'serialNumber': _to_str(serialNumber), + 'idProduct': idProduct, + 'idVendor': idVendor, + 'device_address': device_address, + 'bus_number': bus_number, + 'uid': dev_uid_found, + } + logger.debug(f"Device info: {self._info}") uvc.uvc_free_device_descriptor(desc) break idx +=1 uvc.uvc_free_device_list(dev_list, 1) if dev == NULL: - raise DeviceNotFoundError("Device with uid: '%s' not found"%dev_uid) + raise DeviceNotFoundError(f"Device with uid: `{dev_uid}` not found") #once found we open the device self.dev = dev error = uvc.uvc_open(self.dev, &self.devh, SHOULD_DETACH_KERNEL_DRIVER) if error != uvc.UVC_SUCCESS: - raise OpenError("could not open device. Error:%s"%uvc_error_codes[error]) + raise OpenError(f"Could not open device. Error: {uvc_error_codes[error]}") logger.debug("Device '%s' opended."%dev_uid) cdef _de_init_device(self): From aa28d6b929e5e38fab56e9a8a1aa207d20bc20fa Mon Sep 17 00:00:00 2001 From: Pablo Prietz Date: Mon, 5 Sep 2022 11:17:43 +0200 Subject: [PATCH 106/176] build-and-test: Don't require full rebuild; add direct-install option --- scripts/build-and-test.sh | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/scripts/build-and-test.sh b/scripts/build-and-test.sh index 2c22e02..bf7c01b 100755 --- a/scripts/build-and-test.sh +++ b/scripts/build-and-test.sh @@ -1,15 +1,25 @@ #!/bin/bash -xe -git clean -dxf -e .venv/ + +if [ "$1" == "clean" ] +then + git clean -dxf -e .venv/ +fi + export PKG_CONFIG_PATH=/opt/homebrew/opt/jpeg-turbo/lib/pkgconfig +DIRECT_INSTALL=true BUILD_WHEEL=true -if [ "$BUILD_WHEEL" = true ] + +if [ "$DIRECT_INSTALL" = true ] +then + pip install -vv . --no-build-isolation +elif [ "$BUILD_WHEEL" = true ] then - py -m build . --wheel + python -m build . --wheel -n delocate-wheel -v dist/*.whl - pip install -vv dist/*.whl --force-reinstall + pip install -v dist/*.whl --force-reinstall else - py -m build . --sdist -n + python -m build . --sdist -n pip install -vv dist/*.tar.gz --force-reinstall fi python examples/access_100_frames.py \ No newline at end of file From e10de182c870825e464ed789bbcf751de55c0f3a Mon Sep 17 00:00:00 2001 From: Pablo Prietz Date: Mon, 5 Sep 2022 11:21:04 +0200 Subject: [PATCH 107/176] Correctly configure RPATH on linux --- pyuvc-source/CMakeLists.txt | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/pyuvc-source/CMakeLists.txt b/pyuvc-source/CMakeLists.txt index ac499db..48fef28 100644 --- a/pyuvc-source/CMakeLists.txt +++ b/pyuvc-source/CMakeLists.txt @@ -6,7 +6,12 @@ project(pyuvc ) add_subdirectory(../libuvc-source skbuild-libuvc) -set(CMAKE_INSTALL_RPATH @loader_path/${CMAKE_INSTALL_LIBDIR}) + +if(APPLE) + set(CMAKE_INSTALL_RPATH @loader_path/${CMAKE_INSTALL_LIBDIR}) +elseif(UNIX) + set(CMAKE_INSTALL_RPATH $ORIGIN/${CMAKE_INSTALL_LIBDIR}) +endif() # find skbuild cmake packages find_package(PythonExtensions REQUIRED) From c09d4060689532f993b3469348f42b74204d45fd Mon Sep 17 00:00:00 2001 From: Pablo Prietz Date: Tue, 6 Sep 2022 12:28:23 +0200 Subject: [PATCH 108/176] Add additional frame formats from upstream --- pyuvc-source/cuvc.pxd | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/pyuvc-source/cuvc.pxd b/pyuvc-source/cuvc.pxd index c03ae17..114182a 100644 --- a/pyuvc-source/cuvc.pxd +++ b/pyuvc-source/cuvc.pxd @@ -117,8 +117,17 @@ cdef extern from "libuvc/libuvc.h": UVC_FRAME_FORMAT_RGB UVC_FRAME_FORMAT_BGR UVC_FRAME_FORMAT_MJPEG + UVC_FRAME_FORMAT_H264 UVC_FRAME_FORMAT_GRAY8 + UVC_FRAME_FORMAT_GRAY16 UVC_FRAME_FORMAT_BY8 + UVC_FRAME_FORMAT_BA81 + UVC_FRAME_FORMAT_SGRBG8 + UVC_FRAME_FORMAT_SGBRG8 + UVC_FRAME_FORMAT_SRGGB8 + UVC_FRAME_FORMAT_SBGGR8 + UVC_FRAME_FORMAT_NV12 + UVC_FRAME_FORMAT_P010 UVC_FRAME_FORMAT_COUNT enum: From f2b03242ab61a23b22a20e8fec6e040c05087fb0 Mon Sep 17 00:00:00 2001 From: Pablo Prietz Date: Tue, 6 Sep 2022 12:28:35 +0200 Subject: [PATCH 109/176] Update libuvc --- libuvc-source | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libuvc-source b/libuvc-source index 1dba491..3197510 160000 --- a/libuvc-source +++ b/libuvc-source @@ -1 +1 @@ -Subproject commit 1dba4913271d94e5cf851379c4f0d41872a60091 +Subproject commit 3197510e74535e7540597a8f2fbf068ee7f7f0ad From f43582d4ddaa5c5395dfbabeac5c569608be0f18 Mon Sep 17 00:00:00 2001 From: Pablo Prietz Date: Tue, 6 Sep 2022 12:29:36 +0200 Subject: [PATCH 110/176] Expose additional uvc_format_desc fields --- pyuvc-source/cuvc.pxd | 20 ++++++++------------ 1 file changed, 8 insertions(+), 12 deletions(-) diff --git a/pyuvc-source/cuvc.pxd b/pyuvc-source/cuvc.pxd index 114182a..ddd86c2 100644 --- a/pyuvc-source/cuvc.pxd +++ b/pyuvc-source/cuvc.pxd @@ -186,18 +186,14 @@ cdef extern from "libuvc/libuvc.h": uvc_vs_desc_subtype bDescriptorSubtype uint8_t bFormatIndex uint8_t bNumFrameDescriptors - #union { - #uint8_t guidFormat[16] - #uint8_t fourccFormat[4] - #} - #/** Format-specific data */ - #union { - #/** BPP for uncompressed stream */ - #uint8_t bBitsPerPixel - #/** Flags for JPEG stream */ - #uint8_t bmFlags - #} - #/** Default {uvc_frame_desc} to choose given this format */ + uint8_t guidFormat[16] + uint8_t fourccFormat[4] + # /** Format-specific data */ + # /** BPP for uncompressed stream */ + uint8_t bBitsPerPixel + # /** Flags for JPEG stream */ + uint8_t bmFlags + # /** Default {uvc_frame_desc} to choose given this format */ uint8_t bDefaultFrameIndex uint8_t bAspectRatioX uint8_t bAspectRatioY From 9aca952af49b7e593d721c14f59285526a203473 Mon Sep 17 00:00:00 2001 From: Pablo Prietz Date: Tue, 6 Sep 2022 12:29:50 +0200 Subject: [PATCH 111/176] Expose uvc_stream_ctrl fields --- pyuvc-source/cuvc.pxd | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/pyuvc-source/cuvc.pxd b/pyuvc-source/cuvc.pxd index ddd86c2..7a731d9 100644 --- a/pyuvc-source/cuvc.pxd +++ b/pyuvc-source/cuvc.pxd @@ -361,7 +361,23 @@ cdef extern from "libuvc/libuvc.h": cdef struct uvc_stream_ctrl: - pass + uint16_t bmHint + uint8_t bFormatIndex + uint8_t bFrameIndex + uint32_t dwFrameInterval + uint16_t wKeyFrameRate + uint16_t wPFrameRate + uint16_t wCompQuality + uint16_t wCompWindowSize + uint16_t wDelay + uint32_t dwMaxVideoFrameSize + uint32_t dwMaxPayloadTransferSize + uint32_t dwClockFrequency + uint8_t bmFramingInfo + uint8_t bPreferredVersion + uint8_t bMinVersion + uint8_t bMaxVersion + uint8_t bInterfaceNumber ctypedef uvc_stream_ctrl uvc_stream_ctrl_t From 545be174029976e4bbcf5ddd3342ad95cd33a9e1 Mon Sep 17 00:00:00 2001 From: Pablo Prietz Date: Tue, 6 Sep 2022 12:30:07 +0200 Subject: [PATCH 112/176] Expose uvc_frame_format_for_guid --- pyuvc-source/cuvc.pxd | 1 + 1 file changed, 1 insertion(+) diff --git a/pyuvc-source/cuvc.pxd b/pyuvc-source/cuvc.pxd index 7a731d9..271254c 100644 --- a/pyuvc-source/cuvc.pxd +++ b/pyuvc-source/cuvc.pxd @@ -417,6 +417,7 @@ cdef extern from "libuvc/libuvc.h": const uvc_extension_unit_t *uvc_get_extension_units(uvc_device_handle_t *devh) + uvc_frame_format uvc_frame_format_for_guid(uint8_t guid[16]) uvc_error_t uvc_get_stream_ctrl_format_size( uvc_device_handle_t *devh, uvc_stream_ctrl_t *ctrl, uvc_frame_format format, int width, int height, int fps, int should_detach_kernel_driver) uvc_format_desc_t *uvc_get_format_descs(uvc_device_handle_t* ) From 6e680eb19566e7808c0e41d93d8098741297f998 Mon Sep 17 00:00:00 2001 From: Pablo Prietz Date: Tue, 6 Sep 2022 12:31:42 +0200 Subject: [PATCH 113/176] Correctly load camera formats and request specific stream control --- pyuvc-source/uvc_bindings.pyx | 184 ++++++++++++++++++++-------------- 1 file changed, 106 insertions(+), 78 deletions(-) diff --git a/pyuvc-source/uvc_bindings.pyx b/pyuvc-source/uvc_bindings.pyx index 7cfa15f..2d38511 100644 --- a/pyuvc-source/uvc_bindings.pyx +++ b/pyuvc-source/uvc_bindings.pyx @@ -1,23 +1,17 @@ -''' -(*)~---------------------------------------------------------------------------------- - Pupil - eye tracking platform - Copyright (C) 2012-2015 Pupil Labs - - Distributed under the terms of the CC BY-NC-SA License. - License details are in the file LICENSE, distributed as part of this software. -----------------------------------------------------------------------------------~(*) -''' - import cython +import logging +import platform +import warnings +from itertools import chain from libc.string cimport memset -cimport cuvc as uvc -cimport cturbojpeg as turbojpeg +from typing import NamedTuple, Optional, Tuple + cimport numpy as np import numpy as np + +cimport cuvc as uvc +cimport cturbojpeg as turbojpeg from cuvc cimport uvc_frame_t, timeval -import warnings -import platform -import logging __version__ = "0.15.0" @@ -106,7 +100,20 @@ class DeviceNotFoundError(InitError): def __init__(self, message): super().__init__(message) -__version__ = '0.14' #make sure this is the same in setup.py + +CameraMode = NamedTuple( + "CameraMode", + [ + ("width", int), + ("height", int), + ("fps", int), + ("format_native", int), + ("format_name", str), + ("supported", bool) + ], +) + +_supported_formats = (uvc.UVC_FRAME_FORMAT_GRAY8, uvc.UVC_FRAME_FORMAT_MJPEG) cdef class Frame: @@ -456,8 +463,8 @@ cdef class Capture: cdef uvc.uvc_stream_handle_t *strmh cdef float _bandwidth_factor - cdef tuple _active_mode - cdef list _available_modes + cdef object _active_mode + cdef list _camera_modes cdef dict _info cdef public list controls @@ -468,8 +475,8 @@ cdef class Capture: self._stream_on = 0 self._configured = 0 self.strmh = NULL - self._available_modes = [] - self._active_mode = None,None,None + self._camera_modes = [] + self._active_mode = None self._info = {} self.controls = [] self._bandwidth_factor = 2.0 @@ -554,27 +561,38 @@ cdef class Capture: self._re_init_device() self._start() - cdef _configure_stream(self,mode=(640,480,30)): - cdef int status + cdef _configure_stream(self, mode: Optional[CameraMode] = None): + if mode is None: + if self.available_modes: + mode = self.available_modes[0] + else: + raise InitError( + f"Error: No supported mode found. Unsupported modes: " + f"{self.all_modes}" + ) if self._stream_on: self._stop() + cdef int status + cdef uvc.uvc_frame_format fmt = uvc.UVC_FRAME_FORMAT_ANY status = uvc.uvc_get_stream_ctrl_format_size( self.devh, &self.ctrl, - uvc.UVC_FRAME_FORMAT_COMPRESSED, - mode[0], - mode[1], - mode[2], + mode.format_native, + mode.width, + mode.height, + mode.fps, SHOULD_DETACH_KERNEL_DRIVER, ) if status != uvc.UVC_SUCCESS: - raise InitError("Can't get stream control: Error:'%s'."%uvc_error_codes[status]) + raise InitError(f"Error: Can't get stream control: {uvc_error_codes[status]} (requested {fmt})") + else: + logger.debug(f"Negotiated frame format: {self.ctrl}") + self._configured = 1 self._active_mode = mode - cdef _start(self): cdef int status if not self._configured: @@ -588,7 +606,6 @@ cdef class Capture: self._stream_on = 1 logger.debug("Stream start.") - def stop_stream(self): self._stop() @@ -695,19 +712,31 @@ cdef class Capture: cdef uvc.uvc_format_desc_t *format_desc = uvc.uvc_get_format_descs(self.devh) cdef uvc.uvc_frame_desc *frame_desc cdef int i - self._available_modes = [] + self._camera_modes = [] + cdef uvc.uvc_frame_format format_native + cdef str format_name + while format_desc is not NULL: frame_desc = format_desc.frame_descs + + format_native = uvc.uvc_frame_format_for_guid(format_desc.guidFormat) + format_name = (format_desc.fourccFormat).decode('UTF-8') + is_format_supported = (format_native in _supported_formats) while frame_desc is not NULL: - if frame_desc.bDescriptorSubtype == uvc.UVC_VS_FRAME_MJPEG: - frame_index = frame_desc.bFrameIndex - width,height = frame_desc.wWidth,frame_desc.wHeight - mode = {'size':(width,height),'rates':[]} - i = 0 - while frame_desc.intervals[i]: - mode['rates'].append(interval_to_fps(frame_desc.intervals[i]) ) - i+=1 - self._available_modes.append(mode) + i = 0 + while frame_desc.intervals[i]: + fps = interval_to_fps(frame_desc.intervals[i]) + self._camera_modes.append( + CameraMode( + width=frame_desc.wWidth, + height=frame_desc.wHeight, + fps=fps, + format_native=format_native, + format_name=format_name, + supported=is_format_supported, + ) + ) + i+=1 #go to next frame_desc frame_desc = frame_desc.next @@ -715,10 +744,11 @@ cdef class Capture: #go to next format_desc format_desc = format_desc.next - logger.debug('avaible video modes: %s'%self._available_modes) + logger.debug(f'{self} - all camera modes: {self._camera_modes}') def __str__(self): - return "Capture device \n\t" + "\n\t".join(('%s: %s'%(k,v) for k,v in self._info.iteritems())) + meta = " ".join((f'{k}={v!r}' for k,v in self._info.iteritems())) + return f"Capture({meta})" def close(self): @@ -736,24 +766,25 @@ cdef class Capture: self.close() - property frame_size: - def __get__(self): - return self._active_mode[:2] - def __set__(self,size): - for m in self._available_modes: - if size == m['size']: - if self.frame_rate is not None: - #closest match for rate - rates = [ abs(r-self.frame_rate) for r in m['rates'] ] - best_rate_idx = rates.index(min(rates)) - rate = m['rates'][best_rate_idx] - else: - #fist one - rate = m['rates'][0] - mode = size + (rate,) - self._configure_stream(mode) - return - raise ValueError("Frame size not suported.") + @property + def frame_size(self) -> Tuple[int, int]: + return (self._active_mode.width, self._active_mode.height) + + @frame_size.setter + def frame_size(self, size: Tuple[int, int]): + for m in self._camera_modes: + if size == (m.width, m.height): + if self.frame_rate is not None: + #closest match for rate + rates = [ abs(r-self.frame_rate) for r in m['rates'] ] + best_rate_idx = rates.index(min(rates)) + rate = m['rates'][best_rate_idx] + else: + rate = m['rates'][0] + mode = size + (rate,) + self._configure_stream(mode) + return + raise ValueError("Frame size not suported.") property frame_rate: def __get__(self): @@ -766,35 +797,32 @@ cdef class Capture: property frame_sizes: def __get__(self): - return [m['size'] for m in self._available_modes] + return [m['size'] for m in self._camera_modes] property frame_rates: def __get__(self): - for m in self._available_modes: + for m in self._camera_modes: if m['size'] == self.frame_size: return m['rates'] raise ValueError("Please set frame_size before asking for rates.") - property frame_mode: - def __get__(self): - return self._active_mode - def __set__(self,mode): - logger.debug('Setting mode: %s,%s,%s'%mode) - self._configure_stream(mode) + @property + def frame_mode(self) -> CameraMode: + return self._active_mode - property avaible_modes: - def __get__(self): - warnings.warn("Please use 'available_modes' property", DeprecationWarning) - return self.available_modes + @frame_mode.setter + def frame_mode(self, mode: CameraMode): + logger.debug(f"Setting mode: {mode}") + self._configure_stream(mode) - property available_modes: - def __get__(self): - modes = [] - for idx,m in enumerate(self._available_modes): - for r in m['rates']: - modes.append(m['size']+(r,)) - return modes + @property + def available_modes(self) -> Tuple[CameraMode,...]: + return tuple(mode for mode in self._camera_modes if mode.supported) + + @property + def all_modes(self) -> Tuple[CameraMode,...]: + return tuple(self._camera_modes) property name: def __get__(self): From 1075511261cc4145c1aff08d3887d609eb8ebf8b Mon Sep 17 00:00:00 2001 From: Pablo Prietz Date: Tue, 6 Sep 2022 18:38:56 +0200 Subject: [PATCH 114/176] Update libuvc --- libuvc-source | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libuvc-source b/libuvc-source index 3197510..74e7a96 160000 --- a/libuvc-source +++ b/libuvc-source @@ -1 +1 @@ -Subproject commit 3197510e74535e7540597a8f2fbf068ee7f7f0ad +Subproject commit 74e7a960cfa295fc70a42d8fef8c311484a5f626 From ad09aeeb34d25c033b7c3a28801e57d40f43f99a Mon Sep 17 00:00:00 2001 From: Pablo Prietz Date: Wed, 7 Sep 2022 08:30:41 +0200 Subject: [PATCH 115/176] gitignore all venvs --- .gitignore | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index 59f4983..d3cae46 100644 --- a/.gitignore +++ b/.gitignore @@ -262,7 +262,7 @@ celerybeat.pid # Environments .env -.venv +.venv* env/ venv/ ENV/ From 19247cbfcdfe859952b8c187f2bf4089c0a74388 Mon Sep 17 00:00:00 2001 From: Pablo Prietz Date: Thu, 8 Sep 2022 09:05:52 +0200 Subject: [PATCH 116/176] Add example requirements --- scripts/build-and-test.sh | 10 +++++----- setup.cfg | 3 +++ 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/scripts/build-and-test.sh b/scripts/build-and-test.sh index bf7c01b..56e8f17 100755 --- a/scripts/build-and-test.sh +++ b/scripts/build-and-test.sh @@ -2,24 +2,24 @@ if [ "$1" == "clean" ] then - git clean -dxf -e .venv/ + git clean -dxf -e .venv/ -e .venv-dbg/ fi -export PKG_CONFIG_PATH=/opt/homebrew/opt/jpeg-turbo/lib/pkgconfig +export PKG_CONFIG_PATH=/opt/homebrew/opt/jpeg-turbo/lib/pkgconfig:$PKG_CONFIG_PATH DIRECT_INSTALL=true BUILD_WHEEL=true if [ "$DIRECT_INSTALL" = true ] then - pip install -vv . --no-build-isolation + python -m pip install -v ".[example]" --no-build-isolation elif [ "$BUILD_WHEEL" = true ] then python -m build . --wheel -n delocate-wheel -v dist/*.whl - pip install -v dist/*.whl --force-reinstall + pip install -v "dist/*.whl[example]" --force-reinstall else python -m build . --sdist -n - pip install -vv dist/*.tar.gz --force-reinstall + pip install -vv "dist/*.tar.gz[example]" --force-reinstall fi python examples/access_100_frames.py \ No newline at end of file diff --git a/setup.cfg b/setup.cfg index 0432529..a2abc6b 100644 --- a/setup.cfg +++ b/setup.cfg @@ -33,3 +33,6 @@ testing = pytest-cov pytest-enabler>=1.3 pytest-mypy>=0.9.1;python_implementation != "PyPy" +example = + opencv-python + rich \ No newline at end of file From b618263bd50309490c87dd0eeae10b62339b0956 Mon Sep 17 00:00:00 2001 From: Pablo Prietz Date: Thu, 8 Sep 2022 09:07:21 +0200 Subject: [PATCH 117/176] Adjust debug examples --- examples/access_100_frames.py | 51 +++++++++++++----- examples/vis_two_cameras.py | 97 +++++++++++++++++++++++++++++++++++ 2 files changed, 136 insertions(+), 12 deletions(-) create mode 100644 examples/vis_two_cameras.py diff --git a/examples/access_100_frames.py b/examples/access_100_frames.py index 7ec7866..ff88977 100644 --- a/examples/access_100_frames.py +++ b/examples/access_100_frames.py @@ -1,21 +1,48 @@ -from __future__ import print_function -import uvc +#!python3 + import logging +from rich import print +from rich.logging import RichHandler + +logging.basicConfig( + level=logging.NOTSET, + handlers=[RichHandler(level="DEBUG")], + format="%(message)s", + datefmt="[%X]", +) + +import uvc + + +for device in uvc.device_list(): + + cap = uvc.Capture(device["uid"]) + + for mode in cap.available_modes: + print(f"{cap.name} running at {mode}") + try: + cap.frame_mode = mode + except uvc.InitError as err: + print(f"{cap.name} mode selection - {err}") + continue + try: + for x in range(10): + frame = cap.get_frame_robust() + print("frame gray mean", frame.gray.mean()) + # print(frame.img.mean()) + except uvc.InitError as err: + print(f"{cap.name} getting frames - {err}") -logging.basicConfig(level=logging.INFO) + cap.close() -dev_list = uvc.device_list() -print(dev_list) -cap = uvc.Capture(dev_list[0]["uid"]) # Uncomment the following lines to configure the Pupil 200Hz IR cameras: # controls_dict = dict([(c.display_name, c) for c in cap.controls]) # controls_dict['Auto Exposure Mode'].value = 1 # controls_dict['Gamma'].value = 200 -print(cap.avaible_modes) -cap.frame_mode = cap.avaible_modes[0] -for x in range(100): - frame = cap.get_frame_robust() - print(frame.img.mean()) -cap = None +# cap.frame_mode = cap.avaible_modes[0] +# for x in range(100): +# frame = cap.get_frame_robust() +# print(frame.img.mean()) +# cap = None diff --git a/examples/vis_two_cameras.py b/examples/vis_two_cameras.py new file mode 100644 index 0000000..b98e00e --- /dev/null +++ b/examples/vis_two_cameras.py @@ -0,0 +1,97 @@ +import logging +import os +from typing import Iterable, NamedTuple, Optional + +import cv2 +import uvc.uvc_bindings as uvc +from rich.logging import RichHandler +from rich.traceback import install as install_rich_traceback + + +class CameraSpec(NamedTuple): + name: str + width: int + height: int + fps: int + bandwidth_factor: float = 2.0 + + +def main(camera_specs: Iterable[CameraSpec]): + devices = uvc.device_list() + cameras = {spec: init_camera_from_list(devices, spec) for spec in camera_specs} + if not all(cameras.values()): + raise RuntimeError("Could not initialize all specified cameras") + + try: + keep_running = True + while keep_running: + for spec, cam in cameras.items(): + try: + frame = cam.get_frame(timeout=0.005) + except TimeoutError: + pass + except uvc.InitError as err: + logging.debug(f"Failed to init {spec}: {err}") + keep_running = False + break + except uvc.StreamError as err: + logging.debug(f"Failed to get a frame for {spec}: {err}") + else: + cv2.imshow( + spec.name, frame.bgr if hasattr(frame, "bgr") else frame.gray + ) + if cv2.waitKey(1) & 0xFF == 27: + break + + except KeyboardInterrupt: + pass + + for cam in cameras.values(): + cam.close() + + +def init_camera_from_list(devices, camera: CameraSpec) -> Optional[uvc.Capture]: + logging.debug(f"Searching {camera}...") + for device in devices: + if device["name"] == camera.name: + logging.debug(f"Found match by name") + capture = uvc.Capture(device["uid"]) + capture.bandwidth_factor = camera.bandwidth_factor + for mode in capture.available_modes: + if mode[:3] == camera[1:4]: # compare width, height, fps + capture.frame_mode = mode + return capture + else: + logging.warning( + f"None of the available modes matched: {capture.available_modes}" + ) + capture.close() + else: + logging.warning(f"No matching camera with name {camera.name!r} found") + + +if __name__ == "__main__": + os.environ["LIBUSB_DEBUG"] = "2" + install_rich_traceback() + logging.basicConfig( + level=logging.NOTSET, + handlers=[RichHandler(level="DEBUG")], + format="%(message)s", + datefmt="[%X]", + ) + # logging.getLogger("uvc").setLevel("INFO") + main( + [ + CameraSpec("Redacted1", width=384, height=192, fps=200), + # CameraSpec( + # "Redacted1", + # # width=1280, + # # height=720, + # width=1600, + # height=1200, + # fps=30, + # # bandwidth_factor=0.5, + # bandwidth_factor=1.08, + # ), + ] + ) From 64bc6654bd678af59208da6d996febcc7ef3684e Mon Sep 17 00:00:00 2001 From: Pablo Prietz Date: Thu, 8 Sep 2022 09:07:30 +0200 Subject: [PATCH 118/176] Update libuvc --- libuvc-source | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libuvc-source b/libuvc-source index 74e7a96..d4555aa 160000 --- a/libuvc-source +++ b/libuvc-source @@ -1 +1 @@ -Subproject commit 74e7a960cfa295fc70a42d8fef8c311484a5f626 +Subproject commit d4555aa4595b21e11aa3bcb4978a7518da97c039 From 36b27bfdf56fd2e7dc3810791916d00882871a47 Mon Sep 17 00:00:00 2001 From: Pablo Prietz Date: Thu, 8 Sep 2022 09:07:55 +0200 Subject: [PATCH 119/176] Support GRAY8 frames --- pyuvc-source/uvc_bindings.pyx | 152 +++++++++++++++++++++++----------- 1 file changed, 104 insertions(+), 48 deletions(-) diff --git a/pyuvc-source/uvc_bindings.pyx b/pyuvc-source/uvc_bindings.pyx index 2d38511..3814e9a 100644 --- a/pyuvc-source/uvc_bindings.pyx +++ b/pyuvc-source/uvc_bindings.pyx @@ -117,6 +117,55 @@ _supported_formats = (uvc.UVC_FRAME_FORMAT_GRAY8, uvc.UVC_FRAME_FORMAT_MJPEG) cdef class Frame: + cdef uvc.uvc_frame * _uvc_frame + cdef bint owns_uvc_frame + + cdef public double timestamp + + cdef attach_uvcframe(self,uvc.uvc_frame *uvc_frame,copy=True): + if copy: + self._uvc_frame = uvc.uvc_allocate_frame(uvc_frame.data_bytes) + uvc.uvc_duplicate_frame(uvc_frame,self._uvc_frame) + self.owns_uvc_frame = True + else: + self._uvc_frame = uvc_frame + self.owns_uvc_frame = False + + def __dealloc__(self): + if self.owns_uvc_frame: + uvc.uvc_free_frame(self._uvc_frame) + + property width: + def __get__(self): + return self._uvc_frame.width + + property height: + def __get__(self): + return self._uvc_frame.height + + property index: + def __get__(self): + return self._uvc_frame.sequence + + property gray: + def __get__(self): + cdef np.uint8_t[::1] view + if self._uvc_frame.width * self._uvc_frame.height > self._uvc_frame.data_bytes: + logger.debug( + f"Received less data than expected ({self._uvc_frame.data_bytes} / " + f"{self._uvc_frame.width * self._uvc_frame.height})" + ) + # less data transferred than expected, filling with zeros + view = self._uvc_frame.data + frame = 255 + np.zeros((self._uvc_frame.height * self._uvc_frame.width), dtype=np.uint8) + frame[:self._uvc_frame.data_bytes] = view + else: + view = self._uvc_frame.data + frame = np.asarray(view) + return frame.reshape((self._uvc_frame.height, self._uvc_frame.width)) + + +cdef class MJPEGFrame(Frame): ''' The Frame Object holds image data and image metadata. @@ -136,12 +185,9 @@ cdef class Frame: ''' cdef turbojpeg.tjhandle tj_context - cdef uvc.uvc_frame * _uvc_frame cdef unsigned char[:] _bgr_buffer, _gray_buffer,_yuv_buffer #we use numpy for memory management. cdef bint _yuv_converted, _bgr_converted - cdef public double timestamp cdef public yuv_subsampling - cdef bint owns_uvc_frame def __cinit__(self): self._yuv_converted = False @@ -151,31 +197,6 @@ cdef class Frame: def __init__(self): pass - cdef attach_uvcframe(self,uvc.uvc_frame *uvc_frame,copy=True): - if copy: - self._uvc_frame = uvc.uvc_allocate_frame(uvc_frame.data_bytes) - uvc.uvc_duplicate_frame(uvc_frame,self._uvc_frame) - self.owns_uvc_frame = True - else: - self._uvc_frame = uvc_frame - self.owns_uvc_frame = False - - def __dealloc__(self): - if self.owns_uvc_frame: - uvc.uvc_free_frame(self._uvc_frame) - - property width: - def __get__(self): - return self._uvc_frame.width - - property height: - def __get__(self): - return self._uvc_frame.height - - property index: - def __get__(self): - return self._uvc_frame.sequence - property jpeg_buffer: def __get__(self): cdef np.uint8_t[::1] view = self._uvc_frame.data @@ -575,7 +596,6 @@ cdef class Capture: self._stop() cdef int status - cdef uvc.uvc_frame_format fmt = uvc.UVC_FRAME_FORMAT_ANY status = uvc.uvc_get_stream_ctrl_format_size( self.devh, &self.ctrl, @@ -586,7 +606,10 @@ cdef class Capture: SHOULD_DETACH_KERNEL_DRIVER, ) if status != uvc.UVC_SUCCESS: - raise InitError(f"Error: Can't get stream control: {uvc_error_codes[status]} (requested {fmt})") + raise InitError( + f"Error: Can't get stream control: {uvc_error_codes[status]} " + f"(requested {mode.format_native})" + ) else: logger.debug(f"Negotiated frame format: {self.ctrl}") @@ -600,7 +623,7 @@ cdef class Capture: status = uvc.uvc_stream_open_ctrl(self.devh, &self.strmh, &self.ctrl, SHOULD_DETACH_KERNEL_DRIVER) if status != uvc.UVC_SUCCESS: raise InitError("Can't open stream control: Error:'%s'."%uvc_error_codes[status]) - status = uvc.uvc_stream_start(self.strmh, NULL, NULL,self._bandwidth_factor,0) + status = uvc.uvc_stream_start(self.strmh, NULL, NULL, self._bandwidth_factor, 0) if status != uvc.UVC_SUCCESS: raise InitError("Can't start isochronous stream: Error:'%s'."%uvc_error_codes[status]) self._stream_on = 1 @@ -628,37 +651,70 @@ cdef class Capture: try: frame = self.get_frame() except StreamError as e: - if a: - logger.info('Could not get Frame: "%s". Attempt:%s/%s '%(e.message,a+1,attempts)) - else: - logger.debug('Could not get Frame of first try: "%s". Attempt:%s/%s '%(e.message,a+1,attempts)) + logger.debug( + f"Could not get Frame of first try: '{e}'. Attempt: " + f"{a+1}/{attempts}" + ) else: return frame raise StreamError("Could not grab frame after 3 attempts. Giving up.") def get_frame(self,timeout=0): - cdef int status, j_width,j_height,jpegSubsamp,header_ok + cdef int status, j_width, j_height, jpegSubsamp, header_ok cdef int timeout_usec = int(timeout*1e6) #sec to usec if not self._stream_on: self._start() cdef uvc.uvc_frame *uvc_frame = NULL #when this is called we will overwrite the last jpeg buffer! This can be dangerous! with nogil: - status = uvc.uvc_stream_get_frame(self.strmh,&uvc_frame,timeout_usec) - if status !=uvc.UVC_SUCCESS: + status = uvc.uvc_stream_get_frame(self.strmh, &uvc_frame, timeout_usec) + if status == uvc.UVC_ERROR_TIMEOUT: + raise TimeoutError + elif status != uvc.UVC_SUCCESS: raise StreamError(uvc_error_codes[status]) if uvc_frame is NULL: raise StreamError("Frame pointer is NULL") - ##check jpeg header - header_ok = turbojpeg.tjDecompressHeader2(self.tj_context, uvc_frame.data, uvc_frame.data_bytes, &j_width, &j_height, &jpegSubsamp) - if not (header_ok >=0 and uvc_frame.width == j_width and uvc_frame.height == j_height): - raise StreamError("JPEG header corrupt.") - - cdef Frame out_frame = Frame() - out_frame.tj_context = self.tj_context - out_frame.attach_uvcframe(uvc_frame = uvc_frame,copy=True) - out_frame.timestamp = uvc_frame.capture_time.tv_sec + uvc_frame.capture_time.tv_usec * 1e-6 - return out_frame + + cdef Frame out_frame_gray + cdef MJPEGFrame out_frame_mjpeg + cdef object frame + + if uvc_frame.frame_format == uvc.UVC_COLOR_FORMAT_MJPEG: + ##check jpeg header + header_ok = turbojpeg.tjDecompressHeader2( + self.tj_context, + uvc_frame.data, + uvc_frame.data_bytes, + &j_width, + &j_height, + &jpegSubsamp + ) + if not ( + header_ok >= 0 and + uvc_frame.width == j_width and + uvc_frame.height == j_height + ): + raise StreamError("JPEG header corrupt.") + out_frame_mjpeg = MJPEGFrame() + out_frame_mjpeg.tj_context = self.tj_context + out_frame_mjpeg.attach_uvcframe(uvc_frame=uvc_frame, copy=True) + frame = out_frame_mjpeg + + elif uvc_frame.frame_format == uvc.UVC_COLOR_FORMAT_GRAY8: + # if uvc_frame.width * uvc_frame.height > uvc_frame.data_bytes: + # raise StreamError( + # f"Received too few bytes to fill {uvc_frame.width}x" + # f"{uvc_frame.height} GRAY8 image" + # ) + out_frame_gray = Frame() + out_frame_gray.attach_uvcframe(uvc_frame=uvc_frame, copy=True) + frame = out_frame_gray + + else: + raise NotImplementedError(f"Unsupported frame format {uvc_frame.frame_format}") + + frame.timestamp = uvc_frame.capture_time.tv_sec + uvc_frame.capture_time.tv_usec * 1e-6 + return frame cdef _enumerate_controls(self): From 8e48200a1e113c8389c6b33563bdca97fc954b0f Mon Sep 17 00:00:00 2001 From: Pablo Prietz Date: Thu, 8 Sep 2022 16:03:14 +0200 Subject: [PATCH 120/176] Update libuvc --- libuvc-source | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libuvc-source b/libuvc-source index d4555aa..d6c5b6d 160000 --- a/libuvc-source +++ b/libuvc-source @@ -1 +1 @@ -Subproject commit d4555aa4595b21e11aa3bcb4978a7518da97c039 +Subproject commit d6c5b6d8cc3d1d3ab1a2220adb0d1ff6d028b994 From 68ac67d0c30fe1f856acdbc9c412d821a0c32c59 Mon Sep 17 00:00:00 2001 From: Pablo Prietz Date: Thu, 8 Sep 2022 16:03:36 +0200 Subject: [PATCH 121/176] Example: print available cameras --- examples/vis_two_cameras.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/examples/vis_two_cameras.py b/examples/vis_two_cameras.py index b98e00e..8122c14 100644 --- a/examples/vis_two_cameras.py +++ b/examples/vis_two_cameras.py @@ -20,7 +20,10 @@ def main(camera_specs: Iterable[CameraSpec]): devices = uvc.device_list() cameras = {spec: init_camera_from_list(devices, spec) for spec in camera_specs} if not all(cameras.values()): - raise RuntimeError("Could not initialize all specified cameras") + raise RuntimeError( + "Could not initialize all specified cameras. Available: " + f"{[dev['name'] for dev in devices]}" + ) try: keep_running = True From 7be543a011e1d4a6fe40d74ed235357894f7ca5a Mon Sep 17 00:00:00 2001 From: Pablo Prietz Date: Thu, 8 Sep 2022 16:04:21 +0200 Subject: [PATCH 122/176] Example: Update camera specs --- examples/vis_two_cameras.py | 27 +++++++++++++++++---------- 1 file changed, 17 insertions(+), 10 deletions(-) diff --git a/examples/vis_two_cameras.py b/examples/vis_two_cameras.py index 8122c14..95e547b 100644 --- a/examples/vis_two_cameras.py +++ b/examples/vis_two_cameras.py @@ -74,7 +74,7 @@ def init_camera_from_list(devices, camera: CameraSpec) -> Optional[uvc.Capture]: if __name__ == "__main__": - os.environ["LIBUSB_DEBUG"] = "2" + os.environ["LIBUSB_DEBUG"] = "3" install_rich_traceback() logging.basicConfig( level=logging.NOTSET, @@ -85,16 +85,23 @@ def init_camera_from_list(devices, camera: CameraSpec) -> Optional[uvc.Capture]: # logging.getLogger("uvc").setLevel("INFO") main( [ - CameraSpec("Redacted1", width=384, height=192, fps=200), + CameraSpec( # CameraSpec( - # "Redacted1", - # # width=1280, - # # height=720, - # width=1600, - # height=1200, - # fps=30, + width=384, + height=192, + fps=200, + bandwidth_factor=0, + ), + CameraSpec( # # bandwidth_factor=0.5, - # bandwidth_factor=1.08, - # ), + # width=1280, + # height=720, + width=1600, + height=1200, + fps=30, + # bandwidth_factor=0, + # bandwidth_factor=1.08, + bandwidth_factor=1.6, + ), ] ) From e42ccf6a13131b71086f0ae5ff9ccada056d7f2a Mon Sep 17 00:00:00 2001 From: Pablo Prietz Date: Mon, 12 Sep 2022 16:29:08 +0200 Subject: [PATCH 123/176] Fix frame size and rate getter and setters --- pyuvc-source/uvc_bindings.pyx | 39 +++++++++++++++++++++++------------ 1 file changed, 26 insertions(+), 13 deletions(-) diff --git a/pyuvc-source/uvc_bindings.pyx b/pyuvc-source/uvc_bindings.pyx index 3814e9a..4e1597a 100644 --- a/pyuvc-source/uvc_bindings.pyx +++ b/pyuvc-source/uvc_bindings.pyx @@ -824,32 +824,45 @@ cdef class Capture: @property def frame_size(self) -> Tuple[int, int]: - return (self._active_mode.width, self._active_mode.height) + if self._active_mode: + return (self._active_mode.width, self._active_mode.height) @frame_size.setter def frame_size(self, size: Tuple[int, int]): - for m in self._camera_modes: - if size == (m.width, m.height): + for mode in self.available_modes: + if size == (mode.width, mode.height): if self.frame_rate is not None: #closest match for rate - rates = [ abs(r-self.frame_rate) for r in m['rates'] ] + rates = [abs(m.fps-self.frame_rate) for m in self.available_modes] best_rate_idx = rates.index(min(rates)) - rate = m['rates'][best_rate_idx] - else: - rate = m['rates'][0] - mode = size + (rate,) + mode = self.available_modes[best_rate_idx] self._configure_stream(mode) return raise ValueError("Frame size not suported.") property frame_rate: def __get__(self): - return self._active_mode[2] - def __set__(self,val): - if self._configured: - self.frame_mode = self._active_mode[:2]+(val,) + if self._active_mode: + return self._active_mode.fps + + def __set__(self, val): + if self._active_mode and self._active_mode.fps == val: + return + elif self._active_mode: + for mode in self.available_modes: + if ( + mode.width == self._active_mode.width and + mode.height == self._active_mode.height and + mode.fps == val + ): + self.frame_mode = mode + return + raise ValueError( + f"No available mode with target frame rate {val} Hz found: " + f"{self.available_modes}" + ) else: - raise ValueError('set frame size first.') + logger.warning("Could not set frame rate. Set frame size first.") property frame_sizes: def __get__(self): From 7932e1bcac78e282dd974ff53a8d5b417b829ea5 Mon Sep 17 00:00:00 2001 From: Pablo Prietz Date: Mon, 12 Sep 2022 16:29:15 +0200 Subject: [PATCH 124/176] Update libuvc --- libuvc-source | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libuvc-source b/libuvc-source index d6c5b6d..6ced62f 160000 --- a/libuvc-source +++ b/libuvc-source @@ -1 +1 @@ -Subproject commit d6c5b6d8cc3d1d3ab1a2220adb0d1ff6d028b994 +Subproject commit 6ced62f0b4ef0f842480dfcce40c06b8fff477e1 From d114b7078f387f983a9ed5f2df57eb15cff73ded Mon Sep 17 00:00:00 2001 From: Pablo Prietz Date: Mon, 12 Sep 2022 16:29:58 +0200 Subject: [PATCH 125/176] Do not log error when unable to init a UVC control --- pyuvc-source/uvc_bindings.pyx | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/pyuvc-source/uvc_bindings.pyx b/pyuvc-source/uvc_bindings.pyx index 4e1597a..ee67afb 100644 --- a/pyuvc-source/uvc_bindings.pyx +++ b/pyuvc-source/uvc_bindings.pyx @@ -755,7 +755,11 @@ cdef class Capture: try: control= Control(cap = self,**std_ctl) except Exception as e: - logger.error("Could not init '%s'! Error: %s" %(std_ctl['display_name'],e)) + import traceback + + logger.debug(f"Could not init {std_ctl['display_name']} control!") + logger.debug(f"Control info: {std_ctl}") + logger.debug(traceback.format_exc()) else: self.controls.append(control) From b7747d73fe4e00b13626755fb9a6b951afd7923b Mon Sep 17 00:00:00 2001 From: Pablo Prietz Date: Thu, 15 Sep 2022 10:26:45 +0200 Subject: [PATCH 126/176] Fix frame_sizes/frame_rates properties --- pyuvc-source/uvc_bindings.pyx | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/pyuvc-source/uvc_bindings.pyx b/pyuvc-source/uvc_bindings.pyx index ee67afb..b179579 100644 --- a/pyuvc-source/uvc_bindings.pyx +++ b/pyuvc-source/uvc_bindings.pyx @@ -870,14 +870,17 @@ cdef class Capture: property frame_sizes: def __get__(self): - return [m['size'] for m in self._camera_modes] + return [(m.width, m.height) for m in self._camera_modes] property frame_rates: def __get__(self): - for m in self._camera_modes: - if m['size'] == self.frame_size: - return m['rates'] - raise ValueError("Please set frame_size before asking for rates.") + frame_rates = [ + m.fps for m in self._camera_modes + if (m.width, m.height) == self.frame_size + ] + if not frame_rates: + raise ValueError("Please set frame_size before asking for rates.") + return frame_rates @property From c199059ee42796aad81342aed9b022214c4f6ca0 Mon Sep 17 00:00:00 2001 From: Pablo Prietz Date: Thu, 15 Sep 2022 10:27:39 +0200 Subject: [PATCH 127/176] Add bgr and yuv_buffer properties to Frame for Core compatibility --- pyuvc-source/uvc_bindings.pyx | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/pyuvc-source/uvc_bindings.pyx b/pyuvc-source/uvc_bindings.pyx index b179579..4849165 100644 --- a/pyuvc-source/uvc_bindings.pyx +++ b/pyuvc-source/uvc_bindings.pyx @@ -164,6 +164,19 @@ cdef class Frame: frame = np.asarray(view) return frame.reshape((self._uvc_frame.height, self._uvc_frame.width)) + @property + def bgr(self): + return np.stack([self.gray] * 3, axis=2) + + @property + def yuv_buffer(self): + return None + + #for legacy reasons. + @property + def img(self): + return self.bgr + cdef class MJPEGFrame(Frame): ''' @@ -305,11 +318,6 @@ cdef class MJPEGFrame(Frame): return BGR - #for legacy reasons. - property img: - def __get__(self): - return self.bgr - cdef yuv2bgr(self): #2.75 ms at 1080p cdef int channels = 3 @@ -899,7 +907,6 @@ cdef class Capture: @property def all_modes(self) -> Tuple[CameraMode,...]: return tuple(self._camera_modes) - property name: def __get__(self): return self._info['name'] From 01ca288e4f691c7d160e3d46b2ca00782aa2917c Mon Sep 17 00:00:00 2001 From: Pablo Prietz Date: Thu, 15 Sep 2022 10:34:43 +0200 Subject: [PATCH 128/176] Apply pre-commit --- .gitignore | 2 +- MANIFEST.in | 2 +- WINDOWS_DEVELOPER.md | 6 +-- examples/access_100_frames.py | 85 ++++++++++++++++++----------------- pyuvc-source/CMakeLists.txt | 2 +- pyuvc-source/cturbojpeg.pxd | 2 - pyuvc-source/cuvc.pxd | 6 +-- pyuvc-source/darwin_time.pxi | 3 -- pyuvc-source/linux_time.pxi | 2 +- pyuvc-source/uvc_bindings.pyx | 22 ++++----- pyuvc-source/windows_time.pxi | 7 ++- setup.cfg | 10 ++--- 12 files changed, 75 insertions(+), 74 deletions(-) diff --git a/.gitignore b/.gitignore index d3cae46..5c590ef 100644 --- a/.gitignore +++ b/.gitignore @@ -340,4 +340,4 @@ $RECYCLE.BIN/ # End of https://www.toptal.com/developers/gitignore/api/python,visualstudiocode,macos,windows,linux,pycharm -_skbuild/ \ No newline at end of file +_skbuild/ diff --git a/MANIFEST.in b/MANIFEST.in index 9cf64c5..9221bd8 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -5,4 +5,4 @@ global-exclude *.dll *.so *.dylib global-exclude .DS_Store prune ./.*/ -prune **/__pycache__ \ No newline at end of file +prune **/__pycache__ diff --git a/WINDOWS_DEVELOPER.md b/WINDOWS_DEVELOPER.md index 0ac1a42..b4c4dbe 100644 --- a/WINDOWS_DEVELOPER.md +++ b/WINDOWS_DEVELOPER.md @@ -6,7 +6,7 @@ 2. [Libuvc] (http://github.com/pupil-labs/libuvc) 3. python 3 64-bit 4. MSVS 2015 -5. numpy +5. numpy 6. cython 7. wheel (make sure pip version is latest) @@ -15,10 +15,10 @@ 1. Install turbojpeg VC 64 version: http://netassist.dl.sourceforge.net/project/libjpeg-turbo/1.5.1/libjpeg-turbo-1.5.1-vc64.exe Make sure \bin directory is added to the system path -2. Open setup.py and locate the code block branching from "elif platform.system() == 'Windows':". Edit the uvc_dir , tj_dir, and usb_dir +2. Open setup.py and locate the code block branching from "elif platform.system() == 'Windows':". Edit the uvc_dir , tj_dir, and usb_dir top level directory locations, corresponding to the installation locations of libuvc, libturbojpeg and libusb. If you built luibuvc with binaries directory name different from "bin", and build type different from "Release" , you need to update "uvc_lib" and "include_dirs" as -well. +well. 3. Open a new command prompt (to make sure PATH var is loaded) 4. If you wish to install directly run "python setup.py install" 5. If you'd like to create a wheel, run "pip wheel ." diff --git a/examples/access_100_frames.py b/examples/access_100_frames.py index ff88977..a160205 100644 --- a/examples/access_100_frames.py +++ b/examples/access_100_frames.py @@ -1,48 +1,51 @@ #!python3 import logging + from rich import print from rich.logging import RichHandler -logging.basicConfig( - level=logging.NOTSET, - handlers=[RichHandler(level="DEBUG")], - format="%(message)s", - datefmt="[%X]", -) - -import uvc - - -for device in uvc.device_list(): - - cap = uvc.Capture(device["uid"]) - - for mode in cap.available_modes: - print(f"{cap.name} running at {mode}") - try: - cap.frame_mode = mode - except uvc.InitError as err: - print(f"{cap.name} mode selection - {err}") - continue - try: - for x in range(10): - frame = cap.get_frame_robust() - print("frame gray mean", frame.gray.mean()) - # print(frame.img.mean()) - except uvc.InitError as err: - print(f"{cap.name} getting frames - {err}") - - cap.close() - - -# Uncomment the following lines to configure the Pupil 200Hz IR cameras: -# controls_dict = dict([(c.display_name, c) for c in cap.controls]) -# controls_dict['Auto Exposure Mode'].value = 1 -# controls_dict['Gamma'].value = 200 -# cap.frame_mode = cap.avaible_modes[0] -# for x in range(100): -# frame = cap.get_frame_robust() -# print(frame.img.mean()) -# cap = None +def main(): + import uvc + + for device in uvc.device_list(): + + cap = uvc.Capture(device["uid"]) + + for mode in cap.available_modes: + print(f"{cap.name} running at {mode}") + try: + cap.frame_mode = mode + except uvc.InitError as err: + print(f"{cap.name} mode selection - {err}") + continue + try: + for x in range(10): + frame = cap.get_frame_robust() + print("frame gray mean", frame.gray.mean()) + # print(frame.img.mean()) + except uvc.InitError as err: + print(f"{cap.name} getting frames - {err}") + + cap.close() + + # Uncomment the following lines to configure the Pupil 200Hz IR cameras: + # controls_dict = dict([(c.display_name, c) for c in cap.controls]) + # controls_dict['Auto Exposure Mode'].value = 1 + # controls_dict['Gamma'].value = 200 + + # cap.frame_mode = cap.avaible_modes[0] + # for x in range(100): + # frame = cap.get_frame_robust() + # print(frame.img.mean()) + # cap = None + + +if __name__ == "__main__": + logging.basicConfig( + level=logging.NOTSET, + handlers=[RichHandler(level="DEBUG")], + format="%(message)s", + datefmt="[%X]", + ) diff --git a/pyuvc-source/CMakeLists.txt b/pyuvc-source/CMakeLists.txt index 48fef28..afe4ecd 100644 --- a/pyuvc-source/CMakeLists.txt +++ b/pyuvc-source/CMakeLists.txt @@ -49,4 +49,4 @@ target_link_directories(uvc_bindings PRIVATE ${libturbojpeg_LIBRARY_DIRS}) target_link_libraries(uvc_bindings PLLibUVC::uvc ${libturbojpeg_LIBRARIES}) # install here -install(TARGETS uvc_bindings LIBRARY DESTINATION ".") \ No newline at end of file +install(TARGETS uvc_bindings LIBRARY DESTINATION ".") diff --git a/pyuvc-source/cturbojpeg.pxd b/pyuvc-source/cturbojpeg.pxd index b10241c..f30cc70 100644 --- a/pyuvc-source/cturbojpeg.pxd +++ b/pyuvc-source/cturbojpeg.pxd @@ -267,5 +267,3 @@ cdef extern from "turbojpeg.h": int tjDecompress(tjhandle handle, unsigned char *jpegBuf, long unsigned int jpegSize, unsigned char *dstBuf, int width, int pitch, int height, int pixelSize, int flags) int tjDecompressToYUV(tjhandle handle, unsigned char *jpegBuf, long unsigned int jpegSize, unsigned char *dstBuf, int flags) - - diff --git a/pyuvc-source/cuvc.pxd b/pyuvc-source/cuvc.pxd index 271254c..709e6fb 100644 --- a/pyuvc-source/cuvc.pxd +++ b/pyuvc-source/cuvc.pxd @@ -24,11 +24,11 @@ IF UNAME_SYSNAME == "Windows": cdef extern from "libusb/libusb.h": pass ELIF UNAME_SYSNAME == "Darwin": - from posix.time cimport timeval,timespec + from posix.time cimport timespec, timeval cdef extern from "libusb.h": pass ELSE: - from posix.time cimport timeval,timespec + from posix.time cimport timespec, timeval cdef extern from "libusb-1.0/libusb.h": pass @@ -463,5 +463,3 @@ cdef inline void INT_TO_DW(int32_t i, uint8_t *p): p[1] = i >> 8 p[2] = i >> 16 p[3] = i >> 24 - - diff --git a/pyuvc-source/darwin_time.pxi b/pyuvc-source/darwin_time.pxi index 53c28d1..0748b47 100644 --- a/pyuvc-source/darwin_time.pxi +++ b/pyuvc-source/darwin_time.pxi @@ -21,6 +21,3 @@ cdef double get_sys_time_monotonic(): get_mach_timebase_info(&timeBase) timeConvert = timeBase.numer /timeBase.denom / 1000000000.0 return mach_absolute_time( ) * timeConvert - - - diff --git a/pyuvc-source/linux_time.pxi b/pyuvc-source/linux_time.pxi index abd55fa..51c1eb1 100644 --- a/pyuvc-source/linux_time.pxi +++ b/pyuvc-source/linux_time.pxi @@ -1,4 +1,4 @@ -from posix.time cimport timeval,timespec,clock_gettime,CLOCK_MONOTONIC +from posix.time cimport CLOCK_MONOTONIC, clock_gettime, timespec, timeval cdef double get_sys_time_monotonic(): diff --git a/pyuvc-source/uvc_bindings.pyx b/pyuvc-source/uvc_bindings.pyx index 4849165..eb856fe 100644 --- a/pyuvc-source/uvc_bindings.pyx +++ b/pyuvc-source/uvc_bindings.pyx @@ -1,17 +1,21 @@ -import cython import logging import platform import warnings from itertools import chain + +import cython + from libc.string cimport memset + from typing import NamedTuple, Optional, Tuple cimport numpy as np + import numpy as np -cimport cuvc as uvc cimport cturbojpeg as turbojpeg -from cuvc cimport uvc_frame_t, timeval +cimport cuvc as uvc +from cuvc cimport timeval, uvc_frame_t __version__ = "0.15.0" @@ -57,7 +61,7 @@ else: IS_MACOS_BIG_SUR_OR_OLDER = False cdef int SHOULD_DETACH_KERNEL_DRIVER = int(not IS_MACOS_BIG_SUR_OR_OLDER) -uvc_error_codes = { +uvc_error_codes = { 0:"Success (no error)", -1:"Input/output error", -2:"Invalid parameter", @@ -119,7 +123,7 @@ _supported_formats = (uvc.UVC_FRAME_FORMAT_GRAY8, uvc.UVC_FRAME_FORMAT_MJPEG) cdef class Frame: cdef uvc.uvc_frame * _uvc_frame cdef bint owns_uvc_frame - + cdef public double timestamp cdef attach_uvcframe(self,uvc.uvc_frame *uvc_frame,copy=True): @@ -159,7 +163,7 @@ cdef class Frame: view = self._uvc_frame.data frame = 255 + np.zeros((self._uvc_frame.height * self._uvc_frame.width), dtype=np.uint8) frame[:self._uvc_frame.data_bytes] = view - else: + else: view = self._uvc_frame.data frame = np.asarray(view) return frame.reshape((self._uvc_frame.height, self._uvc_frame.width)) @@ -682,7 +686,7 @@ cdef class Capture: raise StreamError(uvc_error_codes[status]) if uvc_frame is NULL: raise StreamError("Frame pointer is NULL") - + cdef Frame out_frame_gray cdef MJPEGFrame out_frame_mjpeg cdef object frame @@ -786,7 +790,7 @@ cdef class Capture: while format_desc is not NULL: frame_desc = format_desc.frame_descs - + format_native = uvc.uvc_frame_format_for_guid(format_desc.guidFormat) format_name = (format_desc.fourccFormat).decode('UTF-8') is_format_supported = (format_native in _supported_formats) @@ -987,5 +991,3 @@ def is_accessible(dev_uid): uvc.uvc_unref_device(dev) uvc.uvc_exit(ctx) return True - - diff --git a/pyuvc-source/windows_time.pxi b/pyuvc-source/windows_time.pxi index 633832d..7c33b67 100644 --- a/pyuvc-source/windows_time.pxi +++ b/pyuvc-source/windows_time.pxi @@ -1,7 +1,10 @@ import time + #on windows -from libc.stdint cimport uint64_t, int64_t + +from libc.stdint cimport int64_t, uint64_t + cdef extern from "Windows.h": int QueryPerformanceCounter(int64_t *) @@ -15,7 +18,7 @@ cdef double get_sys_time_monotonic(): QueryPerformanceCounter(&ts) QueryPerformanceFrequency(&freq) t_us = (ts * 1000000 ) / freq - + fullsecs = t_us / 1000000 us_left = t_us - fullsecs*1000000 diff --git a/setup.cfg b/setup.cfg index 97ef7eb..db5e3e0 100644 --- a/setup.cfg +++ b/setup.cfg @@ -1,9 +1,9 @@ [metadata] -name = pupil-labs-uvc +name = pupil_labs_uvc description = Usb Video Class Device bindings with format conversion tool -url = https://github.com/pupil-labs/pyuvc long_description = file: README.rst long_description_content_type = text/x-rst +url = https://github.com/pupil-labs/pyuvc author = Pupil Labs GmbH author_email = info@pupil-labs.com license = MIT @@ -27,12 +27,12 @@ docs = jaraco.packaging>=9 rst.linker>=1.9 sphinx +example = + opencv-python + rich testing = pytest>=6 pytest-checkdocs>=2.4 pytest-cov pytest-enabler>=1.3 pytest-mypy>=0.9.1;python_implementation != "PyPy" -example = - opencv-python - rich From fc7713fb3f86d33496e691de052e05dcca1a9835 Mon Sep 17 00:00:00 2001 From: Pablo Prietz Date: Thu, 15 Sep 2022 10:39:22 +0200 Subject: [PATCH 129/176] Add cibuild config --- .github/workflows/main.yml | 62 ++++++++++++++++++++++++++------------ pyproject.toml | 19 ++++++++++++ 2 files changed, 61 insertions(+), 20 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 6ef66e4..7c99eb3 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -8,37 +8,59 @@ on: workflow_dispatch: jobs: - test: + build_wheels: + name: "Build apriltag wheels on ${{ matrix.os }}" strategy: matrix: - python: - - 3.7 - - 3.8 - - 3.9 - - "3.10" - - "3.11" - platform: - - ubuntu-latest - - macos-latest - - windows-latest - runs-on: ${{ matrix.platform }} + os: [windows-latest, ubuntu-latest, macos-latest] + continue-on-error: true + runs-on: ${{ matrix.os }} steps: - uses: actions/checkout@v3 - - name: Setup Python - uses: actions/setup-python@v4 with: - python-version: ${{ matrix.python }}-dev - - name: Install tox + submodules: true + - name: Build wheels + uses: pypa/cibuildwheel@v2.9.0 + + - uses: actions/upload-artifact@v2 + if: always() + with: + name: distribution + path: ./wheelhouse/*.whl + + build_sdist: + name: Build source distribution + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + with: + submodules: true + - uses: actions/setup-python@v4 + with: + python-version: 3.7 + - name: Install build dependencies + run: pip install build + + - name: Build source package + run: python -m build --sdist . + - name: Install test dependencies run: | - python -m pip install tox - - name: Run tests - run: tox + python -m pip install --upgrade pip + pip install tox tox-gh-actions + - name: Test with tox + run: tox --installpkg dist/*.tar.gz + - name: Upload source package + uses: actions/upload-artifact@v3 + with: + name: distribution + path: dist/ check: # This job does nothing and is only used for the branch protection if: always() needs: - - test + - build_wheels + - build_sdist runs-on: ubuntu-latest diff --git a/pyproject.toml b/pyproject.toml index 6fe2b5d..649d051 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -13,3 +13,22 @@ requires = [ build-backend = "setuptools.build_meta" [tool.setuptools_scm] + +[tool.cibuildwheel] +archs = ["native"] +skip = "{cp,pp}27-* {cp,pp}35-* pp*" + +build-frontend = "build" + +test-command = "pytest {package}/tests -v" +before-test = "" +test-requires = ["pytest"] +test-extras = [] + +manylinux-x86_64-image = "manylinux2014" + +[tool.cibuildwheel.windows] +before-build = "pip install delvewheel pupil-pthreads-win" +repair-wheel-command = [ + 'bash scripts/repair-wheels-windows.sh {dest_dir} {wheel}', +] From cc90f1e265c06286e75b00ac6695eda1906ea36e Mon Sep 17 00:00:00 2001 From: Pablo Prietz Date: Thu, 15 Sep 2022 11:06:48 +0200 Subject: [PATCH 130/176] Remove libusb-1.0 header location for Linux --- pyuvc-source/cuvc.pxd | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/pyuvc-source/cuvc.pxd b/pyuvc-source/cuvc.pxd index 271254c..1be05a8 100644 --- a/pyuvc-source/cuvc.pxd +++ b/pyuvc-source/cuvc.pxd @@ -23,13 +23,9 @@ IF UNAME_SYSNAME == "Windows": long tv_nsec cdef extern from "libusb/libusb.h": pass -ELIF UNAME_SYSNAME == "Darwin": - from posix.time cimport timeval,timespec - cdef extern from "libusb.h": - pass ELSE: from posix.time cimport timeval,timespec - cdef extern from "libusb-1.0/libusb.h": + cdef extern from "libusb.h": pass cdef extern from "Python.h": From bf450f1f404bedf02a8bb5c0bd33d71d6d5543e6 Mon Sep 17 00:00:00 2001 From: Pablo Prietz Date: Thu, 15 Sep 2022 15:48:13 +0200 Subject: [PATCH 131/176] Build dependencies for manylinux --- pyproject.toml | 13 +++++++++++++ scripts/build-deps-manylinux.sh | 21 +++++++++++++++++++++ 2 files changed, 34 insertions(+) create mode 100755 scripts/build-deps-manylinux.sh diff --git a/pyproject.toml b/pyproject.toml index 649d051..a7ff533 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -32,3 +32,16 @@ before-build = "pip install delvewheel pupil-pthreads-win" repair-wheel-command = [ 'bash scripts/repair-wheels-windows.sh {dest_dir} {wheel}', ] + +[tool.cibuildwheel.linux] +before-all = [ + "yum install -y libudev-devel", + "{project}/scripts/build-deps-manylinux.sh", +] + +[[tool.cibuildwheel.overrides]] +select = "*-musllinux*" +before-all = [ + "apk add eudev-dev nasm", + "{project}/scripts/build-deps-manylinux.sh", +] diff --git a/scripts/build-deps-manylinux.sh b/scripts/build-deps-manylinux.sh new file mode 100755 index 0000000..6b23f2b --- /dev/null +++ b/scripts/build-deps-manylinux.sh @@ -0,0 +1,21 @@ +#!/bin/bash -xe + +# Build libusb master +git clone https://github.com/libusb/libusb.git /tmp/libusb +pushd /tmp/libusb +./bootstrap.sh +./configure +make -j +make install +popd + +# Build libturbojpeg +pushd /tmp +curl -L -o libjpeg-turbo.tar.gz https://sourceforge.net/projects/libjpeg-turbo/files/1.5.1/libjpeg-turbo-1.5.1.tar.gz/download +tar xvzf libjpeg-turbo.tar.gz +pushd libjpeg-turbo-1.5.1 +./configure --prefix=/usr/local +make -j +make install +popd +popd \ No newline at end of file From 4a0a00941c41adc88a300a9225eb983e42ff4c3b Mon Sep 17 00:00:00 2001 From: Pablo Prietz Date: Thu, 15 Sep 2022 15:53:25 +0200 Subject: [PATCH 132/176] Update libuvc --- libuvc-source | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libuvc-source b/libuvc-source index 6ced62f..4ed65e1 160000 --- a/libuvc-source +++ b/libuvc-source @@ -1 +1 @@ -Subproject commit 6ced62f0b4ef0f842480dfcce40c06b8fff477e1 +Subproject commit 4ed65e1ca8e627b76f890ecd925f471510f0931b From a7ef3a4d569aa39d1f329b1381a5d225747d88ba Mon Sep 17 00:00:00 2001 From: Pablo Prietz Date: Thu, 15 Sep 2022 16:35:34 +0200 Subject: [PATCH 133/176] WIP: Setup Windows variables --- libuvc-source | 2 +- pyproject.toml | 2 +- setup.py | 22 ++++++++++++++++++++++ 3 files changed, 24 insertions(+), 2 deletions(-) diff --git a/libuvc-source b/libuvc-source index 4ed65e1..a12ece9 160000 --- a/libuvc-source +++ b/libuvc-source @@ -1 +1 @@ -Subproject commit 4ed65e1ca8e627b76f890ecd925f471510f0931b +Subproject commit a12ece94c2786b1373c66036383d6fd9ad446f20 diff --git a/pyproject.toml b/pyproject.toml index a7ff533..8584734 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -5,10 +5,10 @@ requires = [ "wheel", "Cython", "numpy", - "pkgconfig", "scikit-build", "cmake", "ninja;platform_system != \"Windows\"", + "pupil_pthreads_win; platform_system == \"Windows\"" ] build-backend = "setuptools.build_meta" diff --git a/setup.py b/setup.py index 2743ec0..9b2f874 100644 --- a/setup.py +++ b/setup.py @@ -1,6 +1,27 @@ +import platform + from setuptools import find_packages from skbuild import setup +cmake_args = [] +if platform.system() == "Windows": + import os + import pupil_pthreads_win as ptw + + cmake_args.append(f"-DPTHREADS_WIN_INCLUDE_DIR='{ptw.include_path}'") + cmake_args.append(f"-DPTHREADS_WIN_IMPORT_LIB_PATH='{ptw.import_lib_path}'") + + for var_name in ("LIBUSB_WIN_INCLUDE_DIR", "LIBUSB_WIN_IMPORT_LIB_PATH"): + var = os.environ.get(var_name) + if var is not None: + cmake_args.append(f"-D{var_name}='{var}'") + + # The Ninja cmake generator will use mingw (gcc) on windows travis instances, but we + # need to use msvc for compatibility. The easiest solution I found was to just use + # the vs cmake generator as it defaults to msvc. + cmake_args.append("-GVisual Studio 15 2017 Win64") + cmake_args.append("-DCMAKE_WINDOWS_EXPORT_ALL_SYMBOLS=True") + pyuvc_source_folder = "pyuvc-source" setup( @@ -9,4 +30,5 @@ include_package_data=False, cmake_source_dir=pyuvc_source_folder, cmake_install_dir=pyuvc_source_folder + "/uvc", + cmake_args=cmake_args, ) From 8e44b35d1c1e4acdb3d7683e87dd2bde70d15ada Mon Sep 17 00:00:00 2001 From: Pablo Prietz Date: Thu, 15 Sep 2022 17:17:51 +0200 Subject: [PATCH 134/176] Fix copy-and-paste error --- .github/workflows/main.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 7c99eb3..112161b 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -9,7 +9,7 @@ on: jobs: build_wheels: - name: "Build apriltag wheels on ${{ matrix.os }}" + name: "Build pyuvc wheels on ${{ matrix.os }}" strategy: matrix: os: [windows-latest, ubuntu-latest, macos-latest] From 9fae41ad5f8fb5f807f06bc48de1eaa563abeec4 Mon Sep 17 00:00:00 2001 From: Pablo Prietz Date: Thu, 15 Sep 2022 17:18:59 +0200 Subject: [PATCH 135/176] Skip building wheels on Windows --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index a7ff533..e08190d 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -16,7 +16,7 @@ build-backend = "setuptools.build_meta" [tool.cibuildwheel] archs = ["native"] -skip = "{cp,pp}27-* {cp,pp}35-* pp*" +skip = "{cp,pp}27-* {cp,pp}35-* pp* *win*" build-frontend = "build" From 081a35373221f562ad3e40afb6dcdebfade3ee8f Mon Sep 17 00:00:00 2001 From: Pablo Prietz Date: Thu, 15 Sep 2022 17:23:57 +0200 Subject: [PATCH 136/176] Disable windows build in GA --- .github/workflows/main.yml | 2 +- pyproject.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 112161b..670f831 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -12,7 +12,7 @@ jobs: name: "Build pyuvc wheels on ${{ matrix.os }}" strategy: matrix: - os: [windows-latest, ubuntu-latest, macos-latest] + os: [ubuntu-latest, macos-latest] continue-on-error: true runs-on: ${{ matrix.os }} steps: diff --git a/pyproject.toml b/pyproject.toml index e08190d..a7ff533 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -16,7 +16,7 @@ build-backend = "setuptools.build_meta" [tool.cibuildwheel] archs = ["native"] -skip = "{cp,pp}27-* {cp,pp}35-* pp* *win*" +skip = "{cp,pp}27-* {cp,pp}35-* pp*" build-frontend = "build" From 89acdd735e14734e6315acfc6c5541b913e8be29 Mon Sep 17 00:00:00 2001 From: Pablo Prietz Date: Fri, 16 Sep 2022 09:31:50 +0200 Subject: [PATCH 137/176] GA Release: Use cibuildwheels instead of pure python wheel --- .github/workflows/main.yml | 6 ++++-- tox.ini | 2 -- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 670f831..14817c0 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -78,10 +78,12 @@ jobs: steps: - uses: actions/checkout@v3 + - uses: actions/download-artifact@v3 + with: + name: distribution + path: dist/ - name: Setup Python uses: actions/setup-python@v4 - with: - python-version: "3.11-dev" - name: Install tox run: | python -m pip install tox diff --git a/tox.ini b/tox.ini index 4426b59..b58c837 100644 --- a/tox.ini +++ b/tox.ini @@ -33,7 +33,5 @@ passenv = setenv = TWINE_USERNAME = {env:TWINE_USERNAME:__token__} commands = - python -c "import shutil; shutil.rmtree('dist', ignore_errors=True)" - python -m build python -m twine upload dist/* python -m jaraco.develop.create-github-release From abec2a2fa935308b19d075053168701b4306c23e Mon Sep 17 00:00:00 2001 From: Pablo Prietz Date: Fri, 16 Sep 2022 10:45:18 +0200 Subject: [PATCH 138/176] Pre-built libuvc --- pyproject.toml | 3 +++ pyuvc-source/CMakeLists.txt | 14 +++++++++----- scripts/build-deps-manylinux.sh | 4 +++- scripts/build-deps-unix.sh | 11 +++++++++++ 4 files changed, 26 insertions(+), 6 deletions(-) create mode 100755 scripts/build-deps-unix.sh diff --git a/pyproject.toml b/pyproject.toml index a7ff533..728acf8 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -33,6 +33,9 @@ repair-wheel-command = [ 'bash scripts/repair-wheels-windows.sh {dest_dir} {wheel}', ] +[tool.cibuildwheel.macos] +before-all = ["{project}/scripts/build-deps-unix.sh"] + [tool.cibuildwheel.linux] before-all = [ "yum install -y libudev-devel", diff --git a/pyuvc-source/CMakeLists.txt b/pyuvc-source/CMakeLists.txt index afe4ecd..5e643eb 100644 --- a/pyuvc-source/CMakeLists.txt +++ b/pyuvc-source/CMakeLists.txt @@ -5,12 +5,16 @@ project(pyuvc HOMEPAGE_URL "https://github.com/pupil-labs/pyuvc" ) -add_subdirectory(../libuvc-source skbuild-libuvc) +find_package(libuvc QUIET) -if(APPLE) - set(CMAKE_INSTALL_RPATH @loader_path/${CMAKE_INSTALL_LIBDIR}) -elseif(UNIX) - set(CMAKE_INSTALL_RPATH $ORIGIN/${CMAKE_INSTALL_LIBDIR}) +if(NOT ${libuvc_FOUND}) + add_subdirectory(../libuvc-source skbuild-libuvc) + + if(APPLE) + set(CMAKE_INSTALL_RPATH @loader_path/${CMAKE_INSTALL_LIBDIR}) + elseif(UNIX) + set(CMAKE_INSTALL_RPATH $ORIGIN/${CMAKE_INSTALL_LIBDIR}) + endif() endif() # find skbuild cmake packages diff --git a/scripts/build-deps-manylinux.sh b/scripts/build-deps-manylinux.sh index 6b23f2b..2a3dda3 100755 --- a/scripts/build-deps-manylinux.sh +++ b/scripts/build-deps-manylinux.sh @@ -18,4 +18,6 @@ pushd libjpeg-turbo-1.5.1 make -j make install popd -popd \ No newline at end of file +popd + +$(dirname "$0")/build-deps-unix.sh \ No newline at end of file diff --git a/scripts/build-deps-unix.sh b/scripts/build-deps-unix.sh new file mode 100755 index 0000000..593e731 --- /dev/null +++ b/scripts/build-deps-unix.sh @@ -0,0 +1,11 @@ +#!/bin/bash -xe + +BUILD_DIR=libuvc-source/build + +rm -rf $BUILD_DIR +mkdir $BUILD_DIR +pushd $BUILD_DIR +cmake .. +cmake --build . +cmake --install . +pushd \ No newline at end of file From a58d3fd69efd22f03af5e7a955ce02a9ccf38a81 Mon Sep 17 00:00:00 2001 From: Pablo Prietz Date: Fri, 16 Sep 2022 10:45:40 +0200 Subject: [PATCH 139/176] GA: Split manylinux and musllinux into two parallel jobs --- .github/workflows/main.yml | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 14817c0..4330e53 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -13,14 +13,22 @@ jobs: strategy: matrix: os: [ubuntu-latest, macos-latest] + include: + - CIBW_BUILD: "*" # default: build everything + - os: ubuntu-latest + CIBW_BUILD: "*manylinux*" + - os: ubuntu-latest + CIBW_BUILD: "*musllinux*" continue-on-error: true runs-on: ${{ matrix.os }} steps: - uses: actions/checkout@v3 with: submodules: true - - name: Build wheels + - name: Build ${{ matrix.CIBW_BUILD }} wheels on ${{ matrix.os }} uses: pypa/cibuildwheel@v2.9.0 + env: + CIBW_BUILD: ${{ matrix.CIBW_BUILD }} - uses: actions/upload-artifact@v2 if: always() From 525065334bcbcf05aee0bb5a715254a657b68fb9 Mon Sep 17 00:00:00 2001 From: Pablo Prietz Date: Fri, 16 Sep 2022 11:13:02 +0200 Subject: [PATCH 140/176] Fix splitting GA for manylinux/musllinux --- .github/workflows/main.yml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 4330e53..6e68577 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -13,8 +13,11 @@ jobs: strategy: matrix: os: [ubuntu-latest, macos-latest] + CIBW_BUILD: ["*"] + exclude: + - os: ubuntu-latest + CIBW_BUILD: "*" include: - - CIBW_BUILD: "*" # default: build everything - os: ubuntu-latest CIBW_BUILD: "*manylinux*" - os: ubuntu-latest From d1ad7d8bb332d62ee93e5fe5d1d011184fb1d3d3 Mon Sep 17 00:00:00 2001 From: Pablo Prietz Date: Fri, 16 Sep 2022 11:56:28 +0200 Subject: [PATCH 141/176] Update libuvc --- libuvc-source | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libuvc-source b/libuvc-source index 4ed65e1..1efbeba 160000 --- a/libuvc-source +++ b/libuvc-source @@ -1 +1 @@ -Subproject commit 4ed65e1ca8e627b76f890ecd925f471510f0931b +Subproject commit 1efbeba7ce25dc477dfdcf23560bb2b8c16cc257 From 5cfd097939a6a7f42b9f49076fc7de3b2c9863db Mon Sep 17 00:00:00 2001 From: Pablo Prietz Date: Fri, 16 Sep 2022 11:57:02 +0200 Subject: [PATCH 142/176] Mention CIBW_BUILD value in job name --- .github/workflows/main.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 6e68577..ff95963 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -9,7 +9,7 @@ on: jobs: build_wheels: - name: "Build pyuvc wheels on ${{ matrix.os }}" + name: "Build ${{ matrix.CIBW_BUILD }} wheels on ${{ matrix.os }}" strategy: matrix: os: [ubuntu-latest, macos-latest] From 498c34dff0a628bbf5a631c65181a6480178d3b0 Mon Sep 17 00:00:00 2001 From: Pablo Prietz Date: Fri, 16 Sep 2022 13:28:12 +0200 Subject: [PATCH 143/176] Set RPATH explicitly to installed libuvc location --- libuvc-source | 2 +- pyuvc-source/CMakeLists.txt | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/libuvc-source b/libuvc-source index 4ed65e1..cb43387 160000 --- a/libuvc-source +++ b/libuvc-source @@ -1 +1 @@ -Subproject commit 4ed65e1ca8e627b76f890ecd925f471510f0931b +Subproject commit cb433876bc4890e16e4cfc59ea621962988bf614 diff --git a/pyuvc-source/CMakeLists.txt b/pyuvc-source/CMakeLists.txt index 5e643eb..4504e8b 100644 --- a/pyuvc-source/CMakeLists.txt +++ b/pyuvc-source/CMakeLists.txt @@ -15,6 +15,9 @@ if(NOT ${libuvc_FOUND}) elseif(UNIX) set(CMAKE_INSTALL_RPATH $ORIGIN/${CMAKE_INSTALL_LIBDIR}) endif() +else() + message(STATUS "Setting RPATH to ${libuvc_LIBRARY_DIRS}") + set(CMAKE_INSTALL_RPATH ${libuvc_LIBRARY_DIRS}) endif() # find skbuild cmake packages From bafe12f171582cf64487ffb9990708eefde36d31 Mon Sep 17 00:00:00 2001 From: Pablo Prietz Date: Fri, 16 Sep 2022 13:54:41 +0200 Subject: [PATCH 144/176] Update libuvc --- libuvc-source | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libuvc-source b/libuvc-source index cb43387..1b8d98c 160000 --- a/libuvc-source +++ b/libuvc-source @@ -1 +1 @@ -Subproject commit cb433876bc4890e16e4cfc59ea621962988bf614 +Subproject commit 1b8d98cb02b0b6422a9921326922e504500122de From e27a3988e19eb331c7a38d66bf6275ad39f3edf8 Mon Sep 17 00:00:00 2001 From: Pablo Prietz Date: Fri, 16 Sep 2022 14:27:10 +0200 Subject: [PATCH 145/176] Update docs --- README.rst | 8 ++++---- docs/conf.py | 2 ++ docs/index.rst | 5 ++++- 3 files changed, 10 insertions(+), 5 deletions(-) diff --git a/README.rst b/README.rst index 74334be..c61a65c 100644 --- a/README.rst +++ b/README.rst @@ -54,10 +54,10 @@ TODO: Update udev rules for running as normal user ************************************* -``` -echo 'SUBSYSTEM=="usb", ENV{DEVTYPE}=="usb_device", GROUP="plugdev", MODE="0664"' | sudo tee /etc/udev/rules.d/10-libuvc.rules > /dev/null -sudo udevadm trigger -``` +.. code-block:: + + echo 'SUBSYSTEM=="usb", ENV{DEVTYPE}=="usb_device", GROUP="plugdev", MODE="0664"' | sudo tee /etc/udev/rules.d/10-libuvc.rules > /dev/null + sudo udevadm trigger Dependencies Mac ################ diff --git a/docs/conf.py b/docs/conf.py index f2bf034..d0a8c59 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -33,3 +33,5 @@ intersphinx_mapping = { "python": ("https://docs.python.org/3", None), } + +html_theme = "furo" diff --git a/docs/index.rst b/docs/index.rst index 7b71b95..3976af9 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -1,13 +1,16 @@ Welcome to |project| documentation! =================================== +.. include:: ../README.rst + :start-line: 26 + .. toctree:: :maxdepth: 1 history -.. automodule:: skeleton +.. automodule:: uvc.uvc_bindings :members: :undoc-members: :show-inheritance: From 40b425508bb4b7c7d7358012547b7794147f4abe Mon Sep 17 00:00:00 2001 From: Pablo Prietz Date: Mon, 19 Sep 2022 14:38:28 +0200 Subject: [PATCH 146/176] Build Windows wheels --- .github/workflows/main.yml | 7 +++-- libuvc-source | 2 +- pyproject.toml | 6 ++-- pyuvc-source/CMakeLists.txt | 56 ++++++++++++++++++++++++++++++----- pyuvc-source/cuvc.pxd | 2 +- pyuvc-source/windows_time.pxi | 9 +++--- scripts/download-deps-win.ps1 | 51 +++++++++++++++++++++++++++++++ scripts/repair-wheels-win.py | 55 ++++++++++++++++++++++++++++++++++ setup.py | 11 ++++--- 9 files changed, 175 insertions(+), 24 deletions(-) create mode 100644 scripts/download-deps-win.ps1 create mode 100644 scripts/repair-wheels-win.py diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index ff95963..30c261f 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -12,7 +12,7 @@ jobs: name: "Build ${{ matrix.CIBW_BUILD }} wheels on ${{ matrix.os }}" strategy: matrix: - os: [ubuntu-latest, macos-latest] + os: [ubuntu-latest, macos-latest, windows-latest] CIBW_BUILD: ["*"] exclude: - os: ubuntu-latest @@ -20,8 +20,9 @@ jobs: include: - os: ubuntu-latest CIBW_BUILD: "*manylinux*" - - os: ubuntu-latest - CIBW_BUILD: "*musllinux*" + # Disable musllinux + # - os: ubuntu-latest + # CIBW_BUILD: "*musllinux*" continue-on-error: true runs-on: ${{ matrix.os }} steps: diff --git a/libuvc-source b/libuvc-source index 1b8d98c..7e1e893 160000 --- a/libuvc-source +++ b/libuvc-source @@ -1 +1 @@ -Subproject commit 1b8d98cb02b0b6422a9921326922e504500122de +Subproject commit 7e1e893b710207472896110c97fd29c1396f401f diff --git a/pyproject.toml b/pyproject.toml index 7f67bdb..92039ce 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -28,10 +28,10 @@ test-extras = [] manylinux-x86_64-image = "manylinux2014" [tool.cibuildwheel.windows] +environment = { DEPS_PATHS_LOC = "tmp/dep_paths.json" } +before-all = "powershell scripts/download-deps-win.ps1 -DEPS_TMP_PATH tmp" before-build = "pip install delvewheel pupil-pthreads-win" -repair-wheel-command = [ - 'bash scripts/repair-wheels-windows.sh {dest_dir} {wheel}', -] +repair-wheel-command = "python scripts/repair-wheels-win.py tmp/dep_paths.json {wheel} {dest_dir}" [tool.cibuildwheel.macos] before-all = ["{project}/scripts/build-deps-unix.sh"] diff --git a/pyuvc-source/CMakeLists.txt b/pyuvc-source/CMakeLists.txt index 4504e8b..cb320c5 100644 --- a/pyuvc-source/CMakeLists.txt +++ b/pyuvc-source/CMakeLists.txt @@ -4,6 +4,7 @@ project(pyuvc DESCRIPTION "Python bindings for the Pupil Labs libuvc library" HOMEPAGE_URL "https://github.com/pupil-labs/pyuvc" ) +cmake_policy(SET CMP0054 NEW) find_package(libuvc QUIET) @@ -15,7 +16,7 @@ if(NOT ${libuvc_FOUND}) elseif(UNIX) set(CMAKE_INSTALL_RPATH $ORIGIN/${CMAKE_INSTALL_LIBDIR}) endif() -else() +elseif(UNIX) message(STATUS "Setting RPATH to ${libuvc_LIBRARY_DIRS}") set(CMAKE_INSTALL_RPATH ${libuvc_LIBRARY_DIRS}) endif() @@ -27,9 +28,31 @@ find_package(Cython REQUIRED) # find external cmake packages find_package(NumPy REQUIRED) -find_package(PkgConfig) -pkg_check_modules(libturbojpeg REQUIRED libturbojpeg) -pkg_check_modules(LIBUSB REQUIRED libusb-1.0) +if(UNIX) + find_package(PkgConfig) + pkg_check_modules(libturbojpeg REQUIRED libturbojpeg) + pkg_check_modules(LIBUSB REQUIRED libusb-1.0) +else() + set(TURBOJPEG_WIN_INCLUDE_DIR "NOTSET" CACHE PATH "Path to the libturbojpeg include dir") + set(TURBOJPEG_WIN_IMPORT_LIB_PATH "NOTSET" CACHE FILEPATH "Path to the libturbojpeg import library") + + list(APPEND required_variables + TURBOJPEG_WIN_INCLUDE_DIR + TURBOJPEG_WIN_IMPORT_LIB_PATH + ) + message(STATUS "Validating config:") + + foreach(VAR IN LISTS required_variables) + if(${VAR} STREQUAL "NOTSET") + message(FATAL_ERROR "Required variable ${VAR} is not set!") + endif() + endforeach() + + foreach(VAR IN LISTS required_variables) + message(STATUS " ${VAR}: ${${VAR}}") + endforeach() + add_definitions("-D_TIMESPEC_DEFINED") +endif() # Note: add_cython_target does not actually add a target, but fills a variable with the # corresponding compiled source file, e.g. here 'uvc_bindings.c'. If only the @@ -47,13 +70,30 @@ python_extension_module(uvc_bindings) target_include_directories(uvc_bindings PRIVATE ".") # include numpy headers -target_include_directories(uvc_bindings PRIVATE ${NumPy_INCLUDE_DIRS}) -target_include_directories(uvc_bindings PRIVATE ${libturbojpeg_INCLUDE_DIRS}) -target_include_directories(uvc_bindings PRIVATE ${LIBUSB_INCLUDE_DIRS}) +target_include_directories( + uvc_bindings + PRIVATE + ${NumPy_INCLUDE_DIRS} + + # Unix + ${LIBUSB_INCLUDE_DIRS} + ${libturbojpeg_INCLUDE_DIRS} + + # Windows vars + ${LIBUSB_WIN_INCLUDE_DIR} + ${TURBOJPEG_WIN_INCLUDE_DIR} +) target_link_directories(uvc_bindings PRIVATE ${libturbojpeg_LIBRARY_DIRS}) -target_link_libraries(uvc_bindings PLLibUVC::uvc ${libturbojpeg_LIBRARIES}) +target_link_libraries( + uvc_bindings + PLLibUVC::uvc + ${libturbojpeg_LIBRARIES} + + # Windows + ${TURBOJPEG_WIN_IMPORT_LIB_PATH} +) # install here install(TARGETS uvc_bindings LIBRARY DESTINATION ".") diff --git a/pyuvc-source/cuvc.pxd b/pyuvc-source/cuvc.pxd index 350b37f..87dc82e 100644 --- a/pyuvc-source/cuvc.pxd +++ b/pyuvc-source/cuvc.pxd @@ -21,7 +21,7 @@ IF UNAME_SYSNAME == "Windows": cdef struct timespec: time_t tv_sec long tv_nsec - cdef extern from "libusb/libusb.h": + cdef extern from "libusb.h": pass ELSE: from posix.time cimport timeval,timespec diff --git a/pyuvc-source/windows_time.pxi b/pyuvc-source/windows_time.pxi index 7c33b67..bce1e30 100644 --- a/pyuvc-source/windows_time.pxi +++ b/pyuvc-source/windows_time.pxi @@ -9,6 +9,7 @@ from libc.stdint cimport int64_t, uint64_t cdef extern from "Windows.h": int QueryPerformanceCounter(int64_t *) int QueryPerformanceFrequency(int64_t *) + cdef double get_sys_time_monotonic(): cdef int64_t ts cdef int64_t freq @@ -17,10 +18,10 @@ cdef double get_sys_time_monotonic(): cdef int64_t us_left QueryPerformanceCounter(&ts) QueryPerformanceFrequency(&freq) - t_us = (ts * 1000000 ) / freq + t_us = ((ts * 1_000_000 ) / freq) - fullsecs = t_us / 1000000 - us_left = t_us - fullsecs*1000000 + fullsecs = (t_us / 1_000_000) + us_left = t_us - fullsecs * 1_000_000 - cdef double secs = fullsecs + us_left/1000000.0 + cdef double secs = fullsecs + us_left / 1_000_000 return secs diff --git a/scripts/download-deps-win.ps1 b/scripts/download-deps-win.ps1 new file mode 100644 index 0000000..1db6efc --- /dev/null +++ b/scripts/download-deps-win.ps1 @@ -0,0 +1,51 @@ +param ($DEPS_TMP_PATH) +if ($DEPS_TMP_PATH -eq $null) { + Exit -1 +} + +rm _skbuild -Recurse -Confirm:$false + +# libturbojpeg +Set-Variable -Name "LTJ_VERSION" -Value "2.1.0" +Set-Variable -Name "LTJ_URL" -Value "https://github.com/pupil-labs/pyndsi/wiki/libjpeg-turbo-${LTJ_VERSION}-vc64.exe" +Set-Variable -Name "LTJ_INSTALLER" -Value "${DEPS_TMP_PATH}\libjpeg-turbo.exe" +Set-Variable -Name "LTJ_INSTALL_PATH" -Value "${DEPS_TMP_PATH}\libjpeg-turbo-install" + +[System.IO.Directory]::CreateDirectory(${LTJ_INSTALL_PATH}) +Write-Output "Downloading libturbojpeg..." +Invoke-WebRequest -Uri ${LTJ_URL} -OutFile ${LTJ_INSTALLER} +Write-Output "Installing libturbojpeg..." +& 'C:\Program Files\7-Zip\7z.exe' x -aoa ${LTJ_INSTALLER} -o"${LTJ_INSTALL_PATH}" + +Set-Variable -Name "LUSB_VERSION" -Value "1.0.26" +Set-Variable -Name "LUSB_BIN_URL" -Value "https://github.com/libusb/libusb/releases/download/v${LUSB_VERSION}/libusb-${LUSB_VERSION}-binaries.7z" +Set-Variable -Name "LUSB_SRC_URL" -Value "https://github.com/libusb/libusb/releases/download/v${LUSB_VERSION}/libusb-${LUSB_VERSION}.tar.bz2" + +Set-Variable -Name "LUSB_BIN_ARCHIVE" -Value "${DEPS_TMP_PATH}\libusb-bin-archive.7z" +Set-Variable -Name "LUSB_SRC_ARCHIVE_TAR_BZ2" -Value "${DEPS_TMP_PATH}\libusb-src-archive.tar.bz2" +Set-Variable -Name "LUSB_SRC_ARCHIVE_TAR" -Value "${DEPS_TMP_PATH}\libusb-src-archive.tar" +Set-Variable -Name "LUSB_INSTALL_PATH" -Value "${DEPS_TMP_PATH}\libusb-install" + +Write-Output "Downloading libusb... ${LUSB_BIN_URL}" +Invoke-WebRequest -Uri ${LUSB_BIN_URL} -OutFile ${LUSB_BIN_ARCHIVE} +Write-Output "Downloading libusb... ${LUSB_SRC_URL}" +Invoke-WebRequest -Uri ${LUSB_SRC_URL} -OutFile ${LUSB_SRC_ARCHIVE_TAR_BZ2} + +Write-Output "Extracting libusb..." +& 'C:\Program Files\7-Zip\7z.exe' x -aoa ${LUSB_BIN_ARCHIVE} -o"${LUSB_INSTALL_PATH}" +& 'C:\Program Files\7-Zip\7z.exe' x -aoa ${LUSB_SRC_ARCHIVE_TAR_BZ2} -o"${DEPS_TMP_PATH}" +& 'C:\Program Files\7-Zip\7z.exe' x -aoa ${LUSB_SRC_ARCHIVE_TAR} -o"${LUSB_INSTALL_PATH}" + +$dep_paths = @{ + LIBUSB_WIN_IMPORT_LIB_PATH = "${LUSB_INSTALL_PATH}/libusb-${LUSB_VERSION}-binaries/VS2015-x64/dll/libusb-1.0.lib" + LIBUSB_WIN_DLL_SEARCH_PATH = "${LUSB_INSTALL_PATH}/libusb-${LUSB_VERSION}-binaries/VS2015-x64/dll" + LIBUSB_WIN_INCLUDE_DIR = "${LUSB_INSTALL_PATH}/libusb-${LUSB_VERSION}/libusb" + TURBOJPEG_WIN_IMPORT_LIB_PATH = "${LTJ_INSTALL_PATH}/lib/turbojpeg.lib" + TURBOJPEG_WIN_DLL_SEARCH_PATH = "${LTJ_INSTALL_PATH}/bin" + TURBOJPEG_WIN_INCLUDE_DIR = "${LTJ_INSTALL_PATH}/include" +} + + +$dep_paths_json = $dep_paths | ConvertTo-Json +$Utf8NoBomEncoding = New-Object System.Text.UTF8Encoding $False +[System.IO.File]::WriteAllLines("${DEPS_TMP_PATH}\dep_paths.json", $dep_paths_json, $Utf8NoBomEncoding) \ No newline at end of file diff --git a/scripts/repair-wheels-win.py b/scripts/repair-wheels-win.py new file mode 100644 index 0000000..93c5592 --- /dev/null +++ b/scripts/repair-wheels-win.py @@ -0,0 +1,55 @@ +import argparse +import json +import pathlib +import shutil +import subprocess + + +def repair(deps_paths_loc, wheel, dest_dir): + import pupil_pthreads_win + + deps_paths = json.loads(pathlib.Path(deps_paths_loc).read_text()) + dll_search_paths = ";".join( + map( + str, + [ + pupil_pthreads_win.dll_path.parent, + *( + p + for key, p in deps_paths.items() + if key.endswith("DLL_SEARCH_PATH") + ), + ], + ) + ) + cmd = [ + "delvewheel.exe", + "repair", + "-w", + dest_dir, + wheel, + "--add-path", + dll_search_paths, + ] + out = subprocess.check_output(cmd).decode() + print(f"+ {cmd}") + print(f"+ delvewheel.exe output:\n{out}") + last_line = out.splitlines()[-1] + + # cibuildwheels expects the wheel to be in dest_dir but delvewheel does not copy the + # wheel to dest_dir if there is nothing to repair + if last_line.startswith("no external dependencies are needed"): + print(f"+ Manually copying {wheel} to {dest_dir}") + pathlib.Path(dest_dir).mkdir(exist_ok=True) + shutil.copy2(wheel, dest_dir) + else: + print(f"+ No need for a manual copy") + + +if __name__ == "__main__": + parser = argparse.ArgumentParser() + parser.add_argument("deps_paths_loc") + parser.add_argument("wheel") + parser.add_argument("dest_dir") + args = parser.parse_args() + repair(args.deps_paths_loc, args.wheel, args.dest_dir) diff --git a/setup.py b/setup.py index 9b2f874..c017577 100644 --- a/setup.py +++ b/setup.py @@ -1,3 +1,5 @@ +import json +import pathlib import platform from setuptools import find_packages @@ -11,10 +13,11 @@ cmake_args.append(f"-DPTHREADS_WIN_INCLUDE_DIR='{ptw.include_path}'") cmake_args.append(f"-DPTHREADS_WIN_IMPORT_LIB_PATH='{ptw.import_lib_path}'") - for var_name in ("LIBUSB_WIN_INCLUDE_DIR", "LIBUSB_WIN_IMPORT_LIB_PATH"): - var = os.environ.get(var_name) - if var is not None: - cmake_args.append(f"-D{var_name}='{var}'") + paths_loc = os.environ.get("DEPS_PATHS_LOC") + paths = json.loads(pathlib.Path(paths_loc).read_text()) + for path_name, path_value in paths.items(): + path_value = pathlib.Path(path_value).resolve() + cmake_args.append(f"-D{path_name}='{path_value}'") # The Ninja cmake generator will use mingw (gcc) on windows travis instances, but we # need to use msvc for compatibility. The easiest solution I found was to just use From cbb1d35e3f18cff73df3a97893851c72dd16c698 Mon Sep 17 00:00:00 2001 From: Pablo Prietz Date: Mon, 19 Sep 2022 14:47:17 +0200 Subject: [PATCH 147/176] Windows GA BUild: Use Visual Studio 17 2022 --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index c017577..dec7eaf 100644 --- a/setup.py +++ b/setup.py @@ -22,7 +22,7 @@ # The Ninja cmake generator will use mingw (gcc) on windows travis instances, but we # need to use msvc for compatibility. The easiest solution I found was to just use # the vs cmake generator as it defaults to msvc. - cmake_args.append("-GVisual Studio 15 2017 Win64") + cmake_args.append("-GVisual Studio 17 2022") cmake_args.append("-DCMAKE_WINDOWS_EXPORT_ALL_SYMBOLS=True") pyuvc_source_folder = "pyuvc-source" From 223626ea36c3dc5ec4db637fb88364f60981917d Mon Sep 17 00:00:00 2001 From: Pablo Prietz Date: Mon, 19 Sep 2022 15:10:00 +0200 Subject: [PATCH 148/176] Windows GA Build: Install vcredist via chocolaty --- scripts/download-deps-win.ps1 | 2 ++ 1 file changed, 2 insertions(+) diff --git a/scripts/download-deps-win.ps1 b/scripts/download-deps-win.ps1 index 1db6efc..cf18a43 100644 --- a/scripts/download-deps-win.ps1 +++ b/scripts/download-deps-win.ps1 @@ -5,6 +5,8 @@ if ($DEPS_TMP_PATH -eq $null) { rm _skbuild -Recurse -Confirm:$false +choco install vcredist-all + # libturbojpeg Set-Variable -Name "LTJ_VERSION" -Value "2.1.0" Set-Variable -Name "LTJ_URL" -Value "https://github.com/pupil-labs/pyndsi/wiki/libjpeg-turbo-${LTJ_VERSION}-vc64.exe" From 6fcfb40c2005d5e6df668868c42624e1feee223f Mon Sep 17 00:00:00 2001 From: Pablo Prietz Date: Fri, 16 Sep 2022 15:09:57 +0200 Subject: [PATCH 149/176] Fix readme test badge --- README.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.rst b/README.rst index c61a65c..5d8b65b 100644 --- a/README.rst +++ b/README.rst @@ -6,7 +6,7 @@ .. _PyPI link: https://pypi.org/project/pupil-labs-uvc -.. image:: https://github.com/jaraco/pupil-labs/pyuvc/tests/badge.svg +.. image:: https://github.com/pupil-labs/pupil-labs/pyuvc/tests/badge.svg :target: https://github.com/pupil-labs/pyuvc/actions?query=workflow%3A%22tests%22 :alt: tests From e5bc586ca2e7d622b22fc984b49ef2f348a0d6a0 Mon Sep 17 00:00:00 2001 From: Pablo Prietz Date: Tue, 20 Sep 2022 13:00:18 +0200 Subject: [PATCH 150/176] Add option to enable libuvc debug logs in pyuvc --- pyuvc-source/CMakeLists.txt | 11 +++++++++-- setup.py | 2 ++ 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/pyuvc-source/CMakeLists.txt b/pyuvc-source/CMakeLists.txt index cb320c5..7c61bcb 100644 --- a/pyuvc-source/CMakeLists.txt +++ b/pyuvc-source/CMakeLists.txt @@ -9,6 +9,12 @@ cmake_policy(SET CMP0054 NEW) find_package(libuvc QUIET) if(NOT ${libuvc_FOUND}) + option(UVC_DEBUGGING "Enable UVC debug logs" OFF) + + if(${UVC_DEBUGGING}) + SET(CMAKE_C_FLAGS "-g -DUVC_DEBUGGING") + endif() + add_subdirectory(../libuvc-source skbuild-libuvc) if(APPLE) @@ -35,7 +41,7 @@ if(UNIX) else() set(TURBOJPEG_WIN_INCLUDE_DIR "NOTSET" CACHE PATH "Path to the libturbojpeg include dir") set(TURBOJPEG_WIN_IMPORT_LIB_PATH "NOTSET" CACHE FILEPATH "Path to the libturbojpeg import library") - + list(APPEND required_variables TURBOJPEG_WIN_INCLUDE_DIR TURBOJPEG_WIN_IMPORT_LIB_PATH @@ -44,13 +50,14 @@ else() foreach(VAR IN LISTS required_variables) if(${VAR} STREQUAL "NOTSET") - message(FATAL_ERROR "Required variable ${VAR} is not set!") + message(FATAL_ERROR "Required variable ${VAR} is not set!") endif() endforeach() foreach(VAR IN LISTS required_variables) message(STATUS " ${VAR}: ${${VAR}}") endforeach() + add_definitions("-D_TIMESPEC_DEFINED") endif() diff --git a/setup.py b/setup.py index dec7eaf..0d807b2 100644 --- a/setup.py +++ b/setup.py @@ -6,8 +6,10 @@ from skbuild import setup cmake_args = [] +cmake_args.append("-DUVC_DEBUGGING=ON") if platform.system() == "Windows": import os + import pupil_pthreads_win as ptw cmake_args.append(f"-DPTHREADS_WIN_INCLUDE_DIR='{ptw.include_path}'") From 816ac8b0ba0c19cf90b5d84d950154aab2b191fd Mon Sep 17 00:00:00 2001 From: Pablo Prietz Date: Wed, 21 Sep 2022 12:18:46 +0200 Subject: [PATCH 151/176] Update libuvc --- libuvc-source | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libuvc-source b/libuvc-source index 7e1e893..e314e84 160000 --- a/libuvc-source +++ b/libuvc-source @@ -1 +1 @@ -Subproject commit 7e1e893b710207472896110c97fd29c1396f401f +Subproject commit e314e8434db520d7be5cbdf766a00611d3951a53 From a50bbfac1dae9c7b8964743891e625a520e024de Mon Sep 17 00:00:00 2001 From: Pablo Prietz Date: Wed, 21 Sep 2022 12:23:17 +0200 Subject: [PATCH 152/176] Build custom libusb on Unix fixing https://github.com/libusb/libusb/issues/1199 --- scripts/build-deps-manylinux.sh | 9 --------- scripts/build-deps-unix.sh | 9 +++++++++ 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/scripts/build-deps-manylinux.sh b/scripts/build-deps-manylinux.sh index 2a3dda3..3a22eb1 100755 --- a/scripts/build-deps-manylinux.sh +++ b/scripts/build-deps-manylinux.sh @@ -1,14 +1,5 @@ #!/bin/bash -xe -# Build libusb master -git clone https://github.com/libusb/libusb.git /tmp/libusb -pushd /tmp/libusb -./bootstrap.sh -./configure -make -j -make install -popd - # Build libturbojpeg pushd /tmp curl -L -o libjpeg-turbo.tar.gz https://sourceforge.net/projects/libjpeg-turbo/files/1.5.1/libjpeg-turbo-1.5.1.tar.gz/download diff --git a/scripts/build-deps-unix.sh b/scripts/build-deps-unix.sh index 593e731..e8e1987 100755 --- a/scripts/build-deps-unix.sh +++ b/scripts/build-deps-unix.sh @@ -1,5 +1,14 @@ #!/bin/bash -xe +# Build libusb master +git clone --branch fix-1199 https://github.com/pupil-labs/libusb.git /tmp/libusb +pushd /tmp/libusb +./bootstrap.sh +./configure +make -j +make install +popd + BUILD_DIR=libuvc-source/build rm -rf $BUILD_DIR From 6a579fd6143fd3ba1395a09c0530e8c5081493bb Mon Sep 17 00:00:00 2001 From: Pablo Prietz Date: Wed, 21 Sep 2022 12:43:44 +0200 Subject: [PATCH 153/176] GA macOS: Install automake to enable libusb build --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 92039ce..2cf8a3c 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -34,7 +34,7 @@ before-build = "pip install delvewheel pupil-pthreads-win" repair-wheel-command = "python scripts/repair-wheels-win.py tmp/dep_paths.json {wheel} {dest_dir}" [tool.cibuildwheel.macos] -before-all = ["{project}/scripts/build-deps-unix.sh"] +before-all = ["brew install automake", "{project}/scripts/build-deps-unix.sh"] [tool.cibuildwheel.linux] before-all = [ From 5932168de2851481434dd22511e71342dc9f4978 Mon Sep 17 00:00:00 2001 From: Pablo Prietz Date: Wed, 21 Sep 2022 12:46:40 +0200 Subject: [PATCH 154/176] Only set debugging cflags on Unix --- libuvc-source | 2 +- pyproject.toml | 2 +- pyuvc-source/CMakeLists.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/libuvc-source b/libuvc-source index e314e84..5deeaf6 160000 --- a/libuvc-source +++ b/libuvc-source @@ -1 +1 @@ -Subproject commit e314e8434db520d7be5cbdf766a00611d3951a53 +Subproject commit 5deeaf67bd8dac1a537074ed00698ec0d777d355 diff --git a/pyproject.toml b/pyproject.toml index 2cf8a3c..6bf0433 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -8,7 +8,7 @@ requires = [ "scikit-build", "cmake", "ninja;platform_system != \"Windows\"", - "pupil_pthreads_win; platform_system == \"Windows\"" + "pupil_pthreads_win; platform_system == \"Windows\"", ] build-backend = "setuptools.build_meta" diff --git a/pyuvc-source/CMakeLists.txt b/pyuvc-source/CMakeLists.txt index 7c61bcb..4242bcc 100644 --- a/pyuvc-source/CMakeLists.txt +++ b/pyuvc-source/CMakeLists.txt @@ -11,7 +11,7 @@ find_package(libuvc QUIET) if(NOT ${libuvc_FOUND}) option(UVC_DEBUGGING "Enable UVC debug logs" OFF) - if(${UVC_DEBUGGING}) + if(${UVC_DEBUGGING} AND UNIX) SET(CMAKE_C_FLAGS "-g -DUVC_DEBUGGING") endif() From b65a0aa167a6721bfeba37fbfc8bffce42d9b442 Mon Sep 17 00:00:00 2001 From: Pablo Prietz Date: Wed, 21 Sep 2022 13:48:12 +0200 Subject: [PATCH 155/176] Fix test badge in readme --- README.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.rst b/README.rst index 5d8b65b..8301c86 100644 --- a/README.rst +++ b/README.rst @@ -6,7 +6,7 @@ .. _PyPI link: https://pypi.org/project/pupil-labs-uvc -.. image:: https://github.com/pupil-labs/pupil-labs/pyuvc/tests/badge.svg +.. image:: https://github.com/pupil-labs/pyuvc/tests/badge.svg :target: https://github.com/pupil-labs/pyuvc/actions?query=workflow%3A%22tests%22 :alt: tests From 999f8f8e133d6add5f1f4dc46ccf3db48ca7386d Mon Sep 17 00:00:00 2001 From: Pablo Prietz Date: Wed, 21 Sep 2022 13:50:46 +0200 Subject: [PATCH 156/176] Fix test badge image --- README.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.rst b/README.rst index 8301c86..c33ae00 100644 --- a/README.rst +++ b/README.rst @@ -6,7 +6,7 @@ .. _PyPI link: https://pypi.org/project/pupil-labs-uvc -.. image:: https://github.com/pupil-labs/pyuvc/tests/badge.svg +.. image:: https://github.com/pupil-labs/pyuvc/actions/workflows/main.yml/badge.svg :target: https://github.com/pupil-labs/pyuvc/actions?query=workflow%3A%22tests%22 :alt: tests From 3748c6ac9343024bec1b1a5a5b8d99ef5698e78d Mon Sep 17 00:00:00 2001 From: Pablo Prietz Date: Wed, 21 Sep 2022 15:28:01 +0200 Subject: [PATCH 157/176] Add libusb as submodule for reproducible builds --- .gitmodules | 4 ++++ libusb-source | 1 + scripts/build-deps-unix.sh | 5 ++--- 3 files changed, 7 insertions(+), 3 deletions(-) create mode 160000 libusb-source diff --git a/.gitmodules b/.gitmodules index 5dfc640..8cb3291 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,3 +1,7 @@ [submodule "libuvc-source"] path = libuvc-source url = git@github.com:pupil-labs/libuvc.git +[submodule "libusb-source"] + path = libusb-source + url = git@github.com:pupil-labs/libusb.git + branch = pl-fixes diff --git a/libusb-source b/libusb-source new file mode 160000 index 0000000..fe4d5cc --- /dev/null +++ b/libusb-source @@ -0,0 +1 @@ +Subproject commit fe4d5ccb3a71094880aef3c6b274a1bc5c7db7c8 diff --git a/scripts/build-deps-unix.sh b/scripts/build-deps-unix.sh index e8e1987..709b62a 100755 --- a/scripts/build-deps-unix.sh +++ b/scripts/build-deps-unix.sh @@ -1,8 +1,7 @@ #!/bin/bash -xe # Build libusb master -git clone --branch fix-1199 https://github.com/pupil-labs/libusb.git /tmp/libusb -pushd /tmp/libusb +pushd libusb-source ./bootstrap.sh ./configure make -j @@ -17,4 +16,4 @@ pushd $BUILD_DIR cmake .. cmake --build . cmake --install . -pushd \ No newline at end of file +pushd From e40910a50fa1405cf1193eee5ef0e5bce319096f Mon Sep 17 00:00:00 2001 From: Pablo Prietz Date: Wed, 21 Sep 2022 17:56:44 +0200 Subject: [PATCH 158/176] Update documentation --- README.rst | 84 +++++++++++++++++++++++++++++------ WINDOWS_DEVELOPER.md | 24 ---------- WINDOWS_USER.md | 7 +-- scripts/download-deps-win.ps1 | 4 +- 4 files changed, 74 insertions(+), 45 deletions(-) delete mode 100644 WINDOWS_DEVELOPER.md diff --git a/README.rst b/README.rst index c33ae00..cd51223 100644 --- a/README.rst +++ b/README.rst @@ -40,34 +40,92 @@ jpeg buffers or have them converted to YUV or Gray or RGB and only when you need The `Frame` class has caching build in to avoid double decompression or conversion. +Install via PyPI +################ + +.. code-block:: + + pip install pupil-labs-uvc Example ####### See `examples/` for code examples. -Dependencies Linux -################## +Install from source +################### -TODO: Update +pyuvc requires the following dependencies: -udev rules for running as normal user -************************************* +- `libusb `__ +- `libturbo-jpeg `__ +- `POSIX Threads for Windows `__ (Windows + only, supplied via the `pupil-pthreads-win `__ + Python module) -.. code-block:: +Once the dependencies are installed, you can pip install the source tree:: + + git clone https://github.com/pupil-labs/pyuvc --recursive + pip install ./pyuvc + +Linux +***** + +Ubuntu/Debian:: + + apt-get update -y + apt-get install -y libusb-1.0-0-dev libturbojpeg-dev + +Running as a non-root user +========================== + +One needs to setup udev rules and add the target user to the ``plugdev`` group to avoid +the privileged access requirement. + +.. code-block:: bash echo 'SUBSYSTEM=="usb", ENV{DEVTYPE}=="usb_device", GROUP="plugdev", MODE="0664"' | sudo tee /etc/udev/rules.d/10-libuvc.rules > /dev/null sudo udevadm trigger + sudo usermod -a -G plugdev $USER + # logout and back in -Dependencies Mac -################ +macOS +***** + +Homebrew:: + + brew update + brew install libusb jpeg-turbo + +Running as a non-root user +========================== -TODO: Update +Unfortunately, this is currently not possible. See +`this libusb issue thread `__ for details. WINDOWS -####### +******* + +Run the following code in a powershell to install the dependencies (requires +`7z `__ to be installed) + +.. code-block:: powershell + + pip install build delvewheel + git clone https://github.com/pupil-labs/pyuvc --recursive + cd pyuvc + scripts/download-deps-win.ps1 -DEPS_TMP_PATH tmp + $Env:DEPS_PATHS_LOC = "tmp/dep_paths.json" + python -m build -w # will create a wheel in dist/ folder; insert the wheel path below + python scripts/repair-wheels-win.py $Env:DEPS_PATHS_LOC wheelhouse + pip install wheelhouse/ + + +Manual driver installation +========================== -TODO: Update +pyuvc requires the libUSBk driver to be installed for your corresponding camera. +Otherwise, metadata like the product name will be set to ``"unknown"``. -Please have a look at WINDOWS_USER.md for install instructions if you want to use PYUVC. -Please have a look at WINDOWS_DEVELOER.md for install instructions if you want to modify PYUVC. +Please see `these instructions `__ +on how to manually install libUSBk drivers for your specific camera. diff --git a/WINDOWS_DEVELOPER.md b/WINDOWS_DEVELOPER.md deleted file mode 100644 index b4c4dbe..0000000 --- a/WINDOWS_DEVELOPER.md +++ /dev/null @@ -1,24 +0,0 @@ -# Windows Installation Instructions for developers - -## Prerequisites - -1. [Libusb with isochronous support] (http://github.com/pupil-labs/libusb) -2. [Libuvc] (http://github.com/pupil-labs/libuvc) -3. python 3 64-bit -4. MSVS 2015 -5. numpy -6. cython -7. wheel (make sure pip version is latest) - - -## Installation - -1. Install turbojpeg VC 64 version: http://netassist.dl.sourceforge.net/project/libjpeg-turbo/1.5.1/libjpeg-turbo-1.5.1-vc64.exe -Make sure \bin directory is added to the system path -2. Open setup.py and locate the code block branching from "elif platform.system() == 'Windows':". Edit the uvc_dir , tj_dir, and usb_dir -top level directory locations, corresponding to the installation locations of libuvc, libturbojpeg and libusb. If you built luibuvc with -binaries directory name different from "bin", and build type different from "Release" , you need to update "uvc_lib" and "include_dirs" as -well. -3. Open a new command prompt (to make sure PATH var is loaded) -4. If you wish to install directly run "python setup.py install" -5. If you'd like to create a wheel, run "pip wheel ." diff --git a/WINDOWS_USER.md b/WINDOWS_USER.md index 1e9ab7e..20e8257 100644 --- a/WINDOWS_USER.md +++ b/WINDOWS_USER.md @@ -1,8 +1,7 @@ -## How to prepare your system for uvc on Windows (8 and later) +# Manual lisbUSBk driver installation PYUVC will only work with Python3.5+ 64bit! - 1. Download and install [libusbk 3.0.7.0] (https://sourceforge.net/projects/libusbk/files/libusbK-release/3.0.7.0/libusbK-3.0.7.0-setup.exe/download) 2. Download and install [Zadig] (https://github.com/pbatard/libwdi/releases/download/v1.2.5/zadig-2.2.exe) @@ -16,7 +15,3 @@ PYUVC will only work with Python3.5+ 64bit! 6. Set the driver replacement to libusbK (v3.0.7.0) and click "Replace driver" 7. Verify in device manager it is correctly replaced. Should be listed under a new category libusbK USB Devices - -8. download the latest wheel file from: https://github.com/pupil-labs/pyuvc/releases and do `pip install --upgrade ` - -At this point the uvc extension should be able to locate and stream from your web camera. diff --git a/scripts/download-deps-win.ps1 b/scripts/download-deps-win.ps1 index cf18a43..ff1ef97 100644 --- a/scripts/download-deps-win.ps1 +++ b/scripts/download-deps-win.ps1 @@ -1,5 +1,6 @@ param ($DEPS_TMP_PATH) if ($DEPS_TMP_PATH -eq $null) { + Write-Error "DEPS_TMP_PATH param not set" Exit -1 } @@ -47,7 +48,6 @@ $dep_paths = @{ TURBOJPEG_WIN_INCLUDE_DIR = "${LTJ_INSTALL_PATH}/include" } - $dep_paths_json = $dep_paths | ConvertTo-Json $Utf8NoBomEncoding = New-Object System.Text.UTF8Encoding $False -[System.IO.File]::WriteAllLines("${DEPS_TMP_PATH}\dep_paths.json", $dep_paths_json, $Utf8NoBomEncoding) \ No newline at end of file +[System.IO.File]::WriteAllLines("${DEPS_TMP_PATH}\dep_paths.json", $dep_paths_json, $Utf8NoBomEncoding) From 51a1bf8f4b1db0cdfa7d3f9fd849355ad1e2e087 Mon Sep 17 00:00:00 2001 From: Pablo Prietz Date: Thu, 22 Sep 2022 11:06:32 +0200 Subject: [PATCH 159/176] Add option to force a local libuvc build --- pyuvc-source/CMakeLists.txt | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/pyuvc-source/CMakeLists.txt b/pyuvc-source/CMakeLists.txt index 4242bcc..2edc548 100644 --- a/pyuvc-source/CMakeLists.txt +++ b/pyuvc-source/CMakeLists.txt @@ -6,10 +6,14 @@ project(pyuvc ) cmake_policy(SET CMP0054 NEW) +option(FORCE_LOCAL_LIBUVC_BUILD "Build local libuvc, even if an installed version was found" OFF) +message(STATUS "FORCE_LOCAL_LIBUVC_BUILD=${FORCE_LOCAL_LIBUVC_BUILD}") + find_package(libuvc QUIET) if(NOT ${libuvc_FOUND}) option(UVC_DEBUGGING "Enable UVC debug logs" OFF) + message(STATUS "UVC_DEBUGGING=${UVC_DEBUGGING}") if(${UVC_DEBUGGING} AND UNIX) SET(CMAKE_C_FLAGS "-g -DUVC_DEBUGGING") From b415db80d474a7998125226f5f2306547fcf9717 Mon Sep 17 00:00:00 2001 From: Pablo Prietz Date: Thu, 22 Sep 2022 11:07:03 +0200 Subject: [PATCH 160/176] Read UVC_DEBUGGING and FORCE_LOCAL_LIBUVC_BUILD values from environment Default to OFF --- setup.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 0d807b2..3a7113b 100644 --- a/setup.py +++ b/setup.py @@ -1,4 +1,5 @@ import json +import os import pathlib import platform @@ -6,7 +7,11 @@ from skbuild import setup cmake_args = [] -cmake_args.append("-DUVC_DEBUGGING=ON") +cmake_args.append(f"-DUVC_DEBUGGING={os.environ.get('UVC_DEBUGGING', 'OFF')}") +cmake_args.append( + f"-DFORCE_LOCAL_LIBUVC_BUILD={os.environ.get('FORCE_LOCAL_LIBUVC_BUILD', 'OFF')}" +) + if platform.system() == "Windows": import os From 3e205d48248aacfb32e547697bb6e74f71f215f0 Mon Sep 17 00:00:00 2001 From: Pablo Prietz Date: Thu, 22 Sep 2022 11:07:35 +0200 Subject: [PATCH 161/176] Add Frame.data_fully_received property --- pyuvc-source/uvc_bindings.pyx | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/pyuvc-source/uvc_bindings.pyx b/pyuvc-source/uvc_bindings.pyx index eb856fe..6be3ae6 100644 --- a/pyuvc-source/uvc_bindings.pyx +++ b/pyuvc-source/uvc_bindings.pyx @@ -139,6 +139,10 @@ cdef class Frame: if self.owns_uvc_frame: uvc.uvc_free_frame(self._uvc_frame) + @property + def data_fully_received(self): + return self._uvc_frame.width * self._uvc_frame.height == self._uvc_frame.data_bytes + property width: def __get__(self): return self._uvc_frame.width @@ -204,9 +208,11 @@ cdef class MJPEGFrame(Frame): cdef turbojpeg.tjhandle tj_context cdef unsigned char[:] _bgr_buffer, _gray_buffer,_yuv_buffer #we use numpy for memory management. cdef bint _yuv_converted, _bgr_converted + cdef bint _data_fully_received cdef public yuv_subsampling def __cinit__(self): + self._data_fully_received = True self._yuv_converted = False self._bgr_converted = False self.tj_context = NULL @@ -357,10 +363,15 @@ cdef class MJPEGFrame(Frame): &self._yuv_buffer[0], 0) if result == -1: + self._data_fully_received = False logger.warning('Turbojpeg jpeg2yuv: %s'%turbojpeg.tjGetErrorStr() ) self.yuv_subsampling = jpegSubsamp self._yuv_converted = True + @property + def data_fully_received(self): + return self._data_fully_received + def clear_caches(self): self._bgr_converted = False self._yuv_converted = False From 2af5ab6e1ec10af028b3472b88997cc0f20cd5f4 Mon Sep 17 00:00:00 2001 From: Pablo Prietz Date: Thu, 22 Sep 2022 11:07:54 +0200 Subject: [PATCH 162/176] Update example --- examples/vis_two_cameras.py | 40 ++++++++++++++++++++----------------- 1 file changed, 22 insertions(+), 18 deletions(-) diff --git a/examples/vis_two_cameras.py b/examples/vis_two_cameras.py index 95e547b..bb0ad8a 100644 --- a/examples/vis_two_cameras.py +++ b/examples/vis_two_cameras.py @@ -1,5 +1,6 @@ import logging import os +import time from typing import Iterable, NamedTuple, Optional import cv2 @@ -27,12 +28,16 @@ def main(camera_specs: Iterable[CameraSpec]): try: keep_running = True + last_update = time.perf_counter() + while keep_running: for spec, cam in cameras.items(): try: - frame = cam.get_frame(timeout=0.005) + frame = cam.get_frame(timeout=0.001) except TimeoutError: pass + # keep_running = False + # break except uvc.InitError as err: logging.debug(f"Failed to init {spec}: {err}") keep_running = False @@ -40,11 +45,14 @@ def main(camera_specs: Iterable[CameraSpec]): except uvc.StreamError as err: logging.debug(f"Failed to get a frame for {spec}: {err}") else: - cv2.imshow( - spec.name, frame.bgr if hasattr(frame, "bgr") else frame.gray - ) - if cv2.waitKey(1) & 0xFF == 27: - break + data = frame.bgr if hasattr(frame, "bgr") else frame.gray + if frame.data_fully_received: + cv2.imshow(spec.name, data) + + if (time.perf_counter() - last_update) > 1 / 60: + if cv2.waitKey(1) & 0xFF == 27: + break + last_update = time.perf_counter() except KeyboardInterrupt: pass @@ -86,22 +94,18 @@ def init_camera_from_list(devices, camera: CameraSpec) -> Optional[uvc.Capture]: main( [ CameraSpec( - # CameraSpec( - width=384, - height=192, - fps=200, - bandwidth_factor=0, - ), - CameraSpec( - # # bandwidth_factor=0.5, - # width=1280, - # height=720, + name=os.environ["CAM1"], width=1600, height=1200, fps=30, - # bandwidth_factor=0, - # bandwidth_factor=1.08, bandwidth_factor=1.6, ), + CameraSpec( + name=os.environ["CAM2"], + width=384, + height=192, + fps=200, + bandwidth_factor=0, + ), ] ) From 6b0317846560ae96686cff29931caf1eb23680fe Mon Sep 17 00:00:00 2001 From: Pablo Prietz Date: Thu, 22 Sep 2022 11:08:52 +0200 Subject: [PATCH 163/176] Update debugging build script --- scripts/build-and-test.sh | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/scripts/build-and-test.sh b/scripts/build-and-test.sh index 56e8f17..e7a46d4 100755 --- a/scripts/build-and-test.sh +++ b/scripts/build-and-test.sh @@ -2,10 +2,14 @@ if [ "$1" == "clean" ] then - git clean -dxf -e .venv/ -e .venv-dbg/ + git clean -dxf -e .venv/ -e .venv-dbg/ -e .tox/ -e uvc_bindings.pyx fi export PKG_CONFIG_PATH=/opt/homebrew/opt/jpeg-turbo/lib/pkgconfig:$PKG_CONFIG_PATH +export PKG_CONFIG_PATH=/Users/ppr/work/pyuvc/libusb-source/libusb-install/lib/pkgconfig:$PKG_CONFIG_PATH + +export FORCE_LOCAL_LIBUVC_BUILD=ON +export UVC_DEBUGGING=OFF DIRECT_INSTALL=true BUILD_WHEEL=true @@ -22,4 +26,4 @@ else python -m build . --sdist -n pip install -vv "dist/*.tar.gz[example]" --force-reinstall fi -python examples/access_100_frames.py \ No newline at end of file +# sudo python examples/access_100_frames.py From b04623b13b1a3275d058c0508c4f666e8c1a61bf Mon Sep 17 00:00:00 2001 From: Pablo Prietz Date: Thu, 22 Sep 2022 12:32:28 +0200 Subject: [PATCH 164/176] Copy GRAY8 frame buffers --- pyuvc-source/uvc_bindings.pyx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyuvc-source/uvc_bindings.pyx b/pyuvc-source/uvc_bindings.pyx index 6be3ae6..07cba5c 100644 --- a/pyuvc-source/uvc_bindings.pyx +++ b/pyuvc-source/uvc_bindings.pyx @@ -169,7 +169,7 @@ cdef class Frame: frame[:self._uvc_frame.data_bytes] = view else: view = self._uvc_frame.data - frame = np.asarray(view) + frame = np.array(view, copy=True) return frame.reshape((self._uvc_frame.height, self._uvc_frame.width)) @property From 7afc00764f6f8c84af64155678fd49c0f7155459 Mon Sep 17 00:00:00 2001 From: Pablo Prietz Date: Thu, 10 Nov 2022 16:14:22 +0100 Subject: [PATCH 165/176] Define submodules urls with https:// --- .gitmodules | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.gitmodules b/.gitmodules index 8cb3291..0eab007 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,7 +1,7 @@ [submodule "libuvc-source"] path = libuvc-source - url = git@github.com:pupil-labs/libuvc.git + url = https://github.com/pupil-labs/libuvc.git [submodule "libusb-source"] path = libusb-source - url = git@github.com:pupil-labs/libusb.git + url = https://github.com/pupil-labs/libusb.git branch = pl-fixes From f3914d23d69cb5e7af42d93264731ff10260da59 Mon Sep 17 00:00:00 2001 From: Pablo Prietz Date: Fri, 11 Nov 2022 09:38:16 +0100 Subject: [PATCH 166/176] Example to iterate over camera modes/resolution/framerates --- examples/print_camera_controls.py | 48 +++++++++++++++++++++++++++++++ 1 file changed, 48 insertions(+) create mode 100644 examples/print_camera_controls.py diff --git a/examples/print_camera_controls.py b/examples/print_camera_controls.py new file mode 100644 index 0000000..5b9a99b --- /dev/null +++ b/examples/print_camera_controls.py @@ -0,0 +1,48 @@ +import logging + +from rich import print +from rich.logging import RichHandler + + +def main(): + import uvc + + devices = uvc.device_list() + print("Available devices", devices) + + for device in devices: + + try: + cap = uvc.Capture(device["uid"]) + except uvc.DeviceNotFoundError: + continue + + print(f"{cap.name}") + + print("Available modes:") + for mode in cap.available_modes: + print( + f"MODE: {mode.width} x {mode.height} @ {mode.fps} ({mode.format_name})" + ) + + print("Iterating over frame sizes and rates") + for res in cap.frame_sizes: + cap.frame_size = res + for rate in cap.frame_rates: + cap.frame_rate = rate + print(f"RES/RATE: {res[0]} x {res[1]} @ {rate} Hz") + + cap.close() + + +if __name__ == "__main__": + # import os + # os.environ["LIBUSB_DEBUG"] = "0" + + logging.basicConfig( + level=logging.NOTSET, + handlers=[RichHandler(level="WARNING")], + format="%(message)s", + datefmt="[%X]", + ) + main() From fdcdb90bee8eae0471fd70b32a7ec28c7d574202 Mon Sep 17 00:00:00 2001 From: Pablo Prietz Date: Fri, 11 Nov 2022 12:48:13 +0100 Subject: [PATCH 167/176] Respect FORCE_LOCAL_LIBUVC_BUILD option --- pyuvc-source/CMakeLists.txt | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/pyuvc-source/CMakeLists.txt b/pyuvc-source/CMakeLists.txt index 2edc548..45a7117 100644 --- a/pyuvc-source/CMakeLists.txt +++ b/pyuvc-source/CMakeLists.txt @@ -9,9 +9,12 @@ cmake_policy(SET CMP0054 NEW) option(FORCE_LOCAL_LIBUVC_BUILD "Build local libuvc, even if an installed version was found" OFF) message(STATUS "FORCE_LOCAL_LIBUVC_BUILD=${FORCE_LOCAL_LIBUVC_BUILD}") -find_package(libuvc QUIET) +if(NOT FORCE_LOCAL_LIBUVC_BUILD) + message(STATUS "Looking for libuvc...") + find_package(libuvc) +endif() -if(NOT ${libuvc_FOUND}) +if(NOT libuvc_FOUND) option(UVC_DEBUGGING "Enable UVC debug logs" OFF) message(STATUS "UVC_DEBUGGING=${UVC_DEBUGGING}") From c0093ec61eca18f93bb72a67993ab6ebdecdd810 Mon Sep 17 00:00:00 2001 From: Pablo Prietz Date: Fri, 11 Nov 2022 12:49:58 +0100 Subject: [PATCH 168/176] Import symbols from uvc_bindings explicitly --- pyuvc-source/uvc/__init__.py | 25 ++++++++++++++++++++++--- 1 file changed, 22 insertions(+), 3 deletions(-) diff --git a/pyuvc-source/uvc/__init__.py b/pyuvc-source/uvc/__init__.py index 58368c6..4731cce 100644 --- a/pyuvc-source/uvc/__init__.py +++ b/pyuvc-source/uvc/__init__.py @@ -5,14 +5,33 @@ except ImportError: from importlib_metadata import PackageNotFoundError, version +from .uvc_bindings import ( + Capture, + Device_List, + InitError, + OpenError, + StreamError, + device_list, + get_time_monotonic, + is_accessible, +) + try: __version__ = version("pupil-labs-uvc") except PackageNotFoundError: # package is not installed pass -__all__ = ["__version__"] - logger = logging.getLogger(__name__) -from .uvc_bindings import * +__all__ = [ + "__version__", + "Capture", + "device_list", + "Device_List", + "get_time_monotonic", + "InitError", + "is_accessible", + "OpenError", + "StreamError", +] From 6ab4c61c6970286c9f3f1a81101eacaa89e7277d Mon Sep 17 00:00:00 2001 From: Pablo Prietz Date: Fri, 11 Nov 2022 12:50:37 +0100 Subject: [PATCH 169/176] Fix enumerating frame sizes and frame rates --- pyuvc-source/uvc_bindings.pyx | 91 ++++++++++++++++++++--------------- 1 file changed, 53 insertions(+), 38 deletions(-) diff --git a/pyuvc-source/uvc_bindings.pyx b/pyuvc-source/uvc_bindings.pyx index 07cba5c..839c552 100644 --- a/pyuvc-source/uvc_bindings.pyx +++ b/pyuvc-source/uvc_bindings.pyx @@ -856,53 +856,68 @@ cdef class Capture: @frame_size.setter def frame_size(self, size: Tuple[int, int]): - for mode in self.available_modes: - if size == (mode.width, mode.height): - if self.frame_rate is not None: - #closest match for rate - rates = [abs(m.fps-self.frame_rate) for m in self.available_modes] - best_rate_idx = rates.index(min(rates)) - mode = self.available_modes[best_rate_idx] - self._configure_stream(mode) - return - raise ValueError("Frame size not suported.") - - property frame_rate: - def __get__(self): - if self._active_mode: - return self._active_mode.fps - - def __set__(self, val): - if self._active_mode and self._active_mode.fps == val: - return - elif self._active_mode: - for mode in self.available_modes: - if ( - mode.width == self._active_mode.width and - mode.height == self._active_mode.height and - mode.fps == val - ): - self.frame_mode = mode - return - raise ValueError( - f"No available mode with target frame rate {val} Hz found: " - f"{self.available_modes}" - ) - else: - logger.warning("Could not set frame rate. Set frame size first.") + logger.debug(f"Changing resolution to target: {size}") + modes_with_target_res = [ + mode for mode in self.available_modes if size == (mode.width, mode.height) + ] + if not modes_with_target_res: + raise ValueError( + f"Frame size {size} not suported. " + f"Available modes: {self.available_modes}" + ) + if self.frame_rate is not None: + # closest match for previously selected rate + rates = [abs(m.fps-self.frame_rate) for m in modes_with_target_res] + best_rate_idx = rates.index(min(rates)) + mode = modes_with_target_res[best_rate_idx] + else: + mode = modes_with_target_res[0] + self.frame_mode = mode + + @property + def frame_rate(self): + if self._active_mode: + return self._active_mode.fps + + @frame_rate.setter + def frame_rate(self, val): + if self._active_mode and self._active_mode.fps == val: + return + elif self._active_mode: + logger.debug(f"Changing frame rate to target: {val}") + for mode in self.available_modes: + if ( + mode.width == self._active_mode.width and + mode.height == self._active_mode.height and + mode.fps == val + ): + self.frame_mode = mode + return + raise ValueError( + f"No available mode with target frame rate {val} Hz found: " + f"{self.available_modes}" + ) + else: + logger.warning("Could not set frame rate. Set frame size first.") property frame_sizes: def __get__(self): - return [(m.width, m.height) for m in self._camera_modes] + return tuple(sorted(set((m.width, m.height) for m in self.available_modes))) property frame_rates: def __get__(self): + frame_size = self.frame_size frame_rates = [ - m.fps for m in self._camera_modes - if (m.width, m.height) == self.frame_size + m.fps for m in self.available_modes + if (m.width, m.height) == frame_size ] if not frame_rates: raise ValueError("Please set frame_size before asking for rates.") + frame_rates = tuple(sorted(set(frame_rates))) + logger.debug( + f"Available frame rates at {frame_size[0]}x{frame_size[1]}: " + f"{frame_rates}" + ) return frame_rates @@ -917,7 +932,7 @@ cdef class Capture: @property def available_modes(self) -> Tuple[CameraMode,...]: - return tuple(mode for mode in self._camera_modes if mode.supported) + return tuple(sorted(mode for mode in self._camera_modes if mode.supported)) @property def all_modes(self) -> Tuple[CameraMode,...]: From 1adb4c74c41c649b1830551c5574409dddd6707b Mon Sep 17 00:00:00 2001 From: Pablo Prietz Date: Mon, 14 Nov 2022 11:39:52 +0100 Subject: [PATCH 170/176] Fix import order --- pyuvc-source/uvc/__init__.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/pyuvc-source/uvc/__init__.py b/pyuvc-source/uvc/__init__.py index 4731cce..050a5cd 100644 --- a/pyuvc-source/uvc/__init__.py +++ b/pyuvc-source/uvc/__init__.py @@ -5,7 +5,10 @@ except ImportError: from importlib_metadata import PackageNotFoundError, version -from .uvc_bindings import ( +# .uvc_bindings expects `logger` to be present +logger = logging.getLogger(__name__) + +from .uvc_bindings import ( # noqa: E402 Capture, Device_List, InitError, @@ -22,8 +25,6 @@ # package is not installed pass -logger = logging.getLogger(__name__) - __all__ = [ "__version__", "Capture", From de3df3e7cb42a0810b98e0e745209c30172911c7 Mon Sep 17 00:00:00 2001 From: Pablo Prietz Date: Tue, 29 Nov 2022 08:40:48 +0100 Subject: [PATCH 171/176] Add `export FORCE_LOCAL_LIBUVC_BUILD=ON` to instr. --- README.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/README.rst b/README.rst index cd51223..f959171 100644 --- a/README.rst +++ b/README.rst @@ -66,6 +66,7 @@ pyuvc requires the following dependencies: Once the dependencies are installed, you can pip install the source tree:: git clone https://github.com/pupil-labs/pyuvc --recursive + export FORCE_LOCAL_LIBUVC_BUILD=ON pip install ./pyuvc Linux From 255f6c3a3e086e31e451b2378c544fb4d2e3821a Mon Sep 17 00:00:00 2001 From: Pablo Prietz Date: Tue, 10 Jan 2023 15:22:52 +0100 Subject: [PATCH 172/176] Add InsufficientBandwidthError --- pyuvc-source/uvc_bindings.pyx | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/pyuvc-source/uvc_bindings.pyx b/pyuvc-source/uvc_bindings.pyx index 839c552..007cdfd 100644 --- a/pyuvc-source/uvc_bindings.pyx +++ b/pyuvc-source/uvc_bindings.pyx @@ -104,6 +104,10 @@ class DeviceNotFoundError(InitError): def __init__(self, message): super().__init__(message) +class InsufficientBandwidthError(InitError): + def __init__(self, message): + super().__init__(message) + CameraMode = NamedTuple( "CameraMode", @@ -643,12 +647,18 @@ cdef class Capture: cdef int status if not self._configured: self._configure_stream() - status = uvc.uvc_stream_open_ctrl(self.devh, &self.strmh, &self.ctrl, SHOULD_DETACH_KERNEL_DRIVER) + status = uvc.uvc_stream_open_ctrl( + self.devh, &self.strmh, &self.ctrl, SHOULD_DETACH_KERNEL_DRIVER + ) if status != uvc.UVC_SUCCESS: - raise InitError("Can't open stream control: Error:'%s'."%uvc_error_codes[status]) + raise InitError( + f"Can't open stream control - error: {uvc_error_codes[status]!r}" + ) status = uvc.uvc_stream_start(self.strmh, NULL, NULL, self._bandwidth_factor, 0) if status != uvc.UVC_SUCCESS: - raise InitError("Can't start isochronous stream: Error:'%s'."%uvc_error_codes[status]) + raise InsufficientBandwidthError( + f"Can't start isochronous stream - error: {uvc_error_codes[status]!r}." + ) self._stream_on = 1 logger.debug("Stream start.") From 4145e8c2f009ae1c23069f3e262b4f945cbd96cd Mon Sep 17 00:00:00 2001 From: Pablo Prietz Date: Thu, 19 Jan 2023 10:42:18 +0100 Subject: [PATCH 173/176] Update libuvc version to 0.1.0 --- libuvc-source | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libuvc-source b/libuvc-source index 5deeaf6..b7181e9 160000 --- a/libuvc-source +++ b/libuvc-source @@ -1 +1 @@ -Subproject commit 5deeaf67bd8dac1a537074ed00698ec0d777d355 +Subproject commit b7181e9625fb19eae80b54818537d2fd0a9090d3 From f29fad4dd65899738e0a0764dcefe807e645459e Mon Sep 17 00:00:00 2001 From: Pablo Prietz Date: Thu, 19 Jan 2023 10:59:28 +0100 Subject: [PATCH 174/176] Update libusb to upstream + pl-fixes --- libusb-source | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libusb-source b/libusb-source index fe4d5cc..ebfec61 160000 --- a/libusb-source +++ b/libusb-source @@ -1 +1 @@ -Subproject commit fe4d5ccb3a71094880aef3c6b274a1bc5c7db7c8 +Subproject commit ebfec61f0a283a7b2b4844b59b9029c9b9e1563d From cb9c548e241f6ecd93754a226910356793e0b740 Mon Sep 17 00:00:00 2001 From: Pablo Prietz Date: Thu, 19 Jan 2023 11:49:54 +0100 Subject: [PATCH 175/176] Remove sdist build step --- .github/workflows/main.yml | 45 +------------------------------------- .gitignore | 1 + MANIFEST.in | 8 ------- 3 files changed, 2 insertions(+), 52 deletions(-) delete mode 100644 MANIFEST.in diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 30c261f..5b3a69e 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -40,51 +40,8 @@ jobs: name: distribution path: ./wheelhouse/*.whl - build_sdist: - name: Build source distribution - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v3 - with: - submodules: true - - uses: actions/setup-python@v4 - with: - python-version: 3.7 - - name: Install build dependencies - run: pip install build - - - name: Build source package - run: python -m build --sdist . - - name: Install test dependencies - run: | - python -m pip install --upgrade pip - pip install tox tox-gh-actions - - name: Test with tox - run: tox --installpkg dist/*.tar.gz - - name: Upload source package - uses: actions/upload-artifact@v3 - with: - name: distribution - path: dist/ - - check: # This job does nothing and is only used for the branch protection - if: always() - - needs: - - build_wheels - - build_sdist - - runs-on: ubuntu-latest - - steps: - - name: Decide whether the needed jobs succeeded or failed - uses: re-actors/alls-green@release/v1 - with: - jobs: ${{ toJSON(needs) }} - release: - needs: - - check + needs: [build_wheels] if: github.event_name == 'push' && contains(github.ref, 'refs/tags/') runs-on: ubuntu-latest diff --git a/.gitignore b/.gitignore index 5c590ef..fd03188 100644 --- a/.gitignore +++ b/.gitignore @@ -181,6 +181,7 @@ share/python-wheels/ .installed.cfg *.egg MANIFEST +MANIFEST.in # PyInstaller # Usually these files are written by a python script from a template diff --git a/MANIFEST.in b/MANIFEST.in deleted file mode 100644 index 9221bd8..0000000 --- a/MANIFEST.in +++ /dev/null @@ -1,8 +0,0 @@ -recursive-include libuvc-source *.h *.c CMakeLists.txt Config.cmake.in -recursive-include pyuvc-source *.h *.pyx CMakeLists.txt - -global-exclude *.dll *.so *.dylib - -global-exclude .DS_Store -prune ./.*/ -prune **/__pycache__ From 045a3b927327172af9d7cd0cd306a26c75feb738 Mon Sep 17 00:00:00 2001 From: Pablo Prietz Date: Thu, 19 Jan 2023 13:18:44 +0100 Subject: [PATCH 176/176] Upgrade actions/download-artifact v2 -> v3 [ci skip] --- .github/workflows/main.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 5b3a69e..a6515d1 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -34,7 +34,7 @@ jobs: env: CIBW_BUILD: ${{ matrix.CIBW_BUILD }} - - uses: actions/upload-artifact@v2 + - uses: actions/upload-artifact@v3 if: always() with: name: distribution