Procházet zdrojové kódy

More thorough dev-docs

default_compile_flags
vector-of-bool před 3 roky
rodič
revize
9d0863434c
13 změnil soubory, kde provedl 790 přidání a 170 odebrání
  1. +1
    -1
      Makefile
  2. +5
    -1
      docs/conf.py
  3. +88
    -164
      docs/dev/building.rst
  4. +79
    -0
      docs/dev/ci-api.rst
  5. +91
    -0
      docs/dev/env.rst
  6. +12
    -1
      docs/dev/index.rst
  7. +29
    -0
      docs/dev/reqs.rst
  8. +74
    -0
      docs/dev/testing.rst
  9. +354
    -1
      poetry.lock
  10. +1
    -0
      pyproject.toml
  11. +2
    -0
      tools/dds_ci/main.py
  12. +12
    -1
      tools/dds_ci/testing/error.py
  13. +42
    -1
      tools/dds_ci/testing/fixtures.py

+ 1
- 1
Makefile Zobrazit soubor

@@ -28,7 +28,7 @@ docs-server: docs
python -m http.server 9794

docs-watch: docs
+sh tools/docs-watch.sh
+poetry run sh tools/docs-watch.sh

docs-sync-server:
mkdir -p _build/docs

+ 5
- 1
docs/conf.py Zobrazit soubor

@@ -14,13 +14,17 @@ version = ''
release = '0.1.0-alpha.5'

# -- General configuration ---------------------------------------------------
extensions = []
extensions = ['sphinx.ext.autodoc', 'sphinx.ext.intersphinx']
templates_path = []
source_suffix = '.rst'
master_doc = 'index'
language = None
exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store']
pygments_style = None
intersphinx_mapping = {
'python': ('https://docs.python.org/3', None),
'pytest': ('https://docs.pytest.org/en/latest/', None),
}

# -- Options for HTML output -------------------------------------------------
html_theme = 'nature'

+ 88
- 164
docs/dev/building.rst Zobrazit soubor

@@ -1,209 +1,133 @@
Building ``dds`` from Source
############################

While prebuilt ``dds`` executables are `available on the GitHub page
<releases_>`_, one may wish to build ``dds`` from source.

.. _releases: https://github.com/vector-of-bool/dds/releases

The ``dds`` build process is designed to be as turn-key simple as possible.


Platform Support
****************

``dds`` aims to be as cross-platform as possible. It currently build and
executes on Windows, macOS, Linux, and FreeBSD. Support for additional
platforms is possible but will require modifications to ``bootstrap.py`` that
will allow it to be built on such platforms.


Build Requirements
******************

Building ``dds`` has a simple set of requirements:

- **Python 3.6** or newer to run the bootstrap/CI scripts.
- A C++ compiler that has rudimentary support for C++20 concepts. Newer
releases of Visual C++ that ship with **VS 2019** will be sufficient on
Windows, as will **GCC 9** with ``-fconcepts`` on other platforms.

.. note::
On Windows, you will need to execute the build from within a Visual C++
enabled environment. This will involve launching the build from a Visual
Studio Command Prompt.

.. note::
At the time of writing, C++20 Concepts has not yet been released in Clang,
but should be available in LLVM/Clang 11 and newer.


Build Scripts and the CI Process
********************************

The main CI process is driven by Python. The root CI script is ``tools/ci.py``,
and it accepts several command-line parameters. Only a few of are immediate
interest:

``--bootstrap-with=<method>`` or ``-B <method>``
Tell ``ci.py`` how to obtain the previous ``dds`` executable that can build
the *current* ``dds`` source tree. This accepts one of three values:
``skip``, ``download``, or ``build``. Refer to :ref:`bootstrapping`.

``--build-only``
A flag that tells ``ci.py`` to exit after it has successfully built the
current source tree, and to not execute the phase-2 build nor the automated
tests.

``--toolchain=<path>`` or ``-T <path>``
Tell ``ci.py`` what toolchain to give to the prior ``dds`` to build the
current ``dds``.

The ``ci.py`` script performs the following actions, in order:

#. Prepare the build output directory
#. Prepare the prior version of ``dds`` that will build the current version.
#. Import the embedded ``catalog.json`` into a catalog database stored within
``_prebuilt/``. This will be used to resolve the third-party packages that
``dds`` itself uses.
#. Invoke the build of ``dds`` using the prebuilt ``dds`` from the prior
bootstrap phase. If ``--build-only`` was specified, the CI script stops
here.
#. Use the new ``dds`` executable to rebuild itself *again* (phase-2 self-build
test). A bit of a "sanity test."
#. Execute the test suite using ``pytest``.


.. _bootstrapping:
This page assumes that you have ready the :doc:`env` page, and that you are
running all commands from within the Poetry-generated virtual environment.

Bootstrapping ``dds``
*********************
The main entrypoint for the ``dds`` CI process is the ``dds-ci`` command, which
will build and test the ``dds`` from the repository sources. ``dds-ci`` accepts
several optional command-line arguments to tweak its behavior.

In the beginning, ``dds`` was built by a Python script that globbed the sources
and invoked the compiler+linker on those sources. Once ``dds`` was able to
build and link itself, this Python script was replaced instead with ``dds``
building itself. ``dds`` has never used another build system.

The ``ci.py`` script accepts one of three methods for the ``--bootstrap-with``
flag: ``skip``, ``download``, or ``build``.
Running a Build *Only*
**********************

Once bootstrapping is complete, a ``dds`` executable will be written to
``_prebuilt/dds``. This executable refers to a **previous** version of ``dds``
that is able to build the newer ``dds`` source tree.
If you only wish to obtain a built ``dds`` executable, the ``--no-test``
parameter can be given::

.. note::
For all development work on ``dds``, the ``_prebuilt/dds`` executable should
always be used. This means that newer ``dds`` features are not available
for use within the ``dds`` repository.
$ dds-ci --no-test

This will skip the audit-build and testing phases of CI and build only the final
``dds`` executable.

Bootstrap: ``skip``
===================

If given ``skip``, ``ci.py`` will not perform any bootstrapping steps. It will
assume that there is an existing ``_prebuilt/dds`` executable. This option
should be used once bootstrapping has been performed at least once with another
method, as this is much faster than rebuilding/redownloading every time.
Rapid Iterations for Development
********************************

If you are making frequent changes to ``dds``'s source code and want a fast
development process, use ``--rapid``::

Bootstrap: ``download``
=======================
$ dds-ci --rapid

The ``ci.py`` script has a reference to a download URL of the prior version of
``dds`` that has been designated for the bootstrap. These executables originate
from `the GitHub releases <releases_>`_ page.
This will build the build step only, and builds an executable with maximum debug
and audit information, including AddressSanitizer and
UndefinedBehaviorSanitizer. This will also execute the unit tests, which should
run completely in under two seconds (if they are slower, then it may be a bug).

If given ``download``, then ``ci.py`` will download a predetermined ``dds``
executable and use it to perform the remainder of the build.

Toolchain Control
*****************

Bootstrap: ``build``
====================
``dds-ci`` will automatically select and build with an appropriate
:doc:`toolchain </guide/toolchains>` based on what it detects of the host
platform, but you may want to tweak those options.

Another script, ``tools/bootstrap.py`` is able to build ``dds`` from the ground
up. It works by progressively cloning previous versions of the ``dds``
repository and using them to build the next commit in the chain.
The ``dds-ci`` script accepts two toolchain options:

While this is a neat trick, it isn't necessary for most development, as the
resulting executable will be derived from the same commit as the executable
that would be obtained using the ``download`` method. This is also more fragile
as the past commits may make certain assumptions about the system that might
not be true outside of the CI environment. The build process may be tweaked in
the future to correct these assumptions.
``--main-toolchain``
This is the toolchain that is used to create a final release-built executable.
If you build with ``--no-test``, this toolchain will be used.

``--test-toolchain`` This is the toolchain that is used to create an auditing
and debuggable executable of ``dds``. This is the toolchain that is used if you
build with ``--rapid``.

Selecting a Build Toolchain
***************************
If you build with neither ``--rapid`` nor ``--no-test``, then ``dds-ci`` will
build *two* ``dds`` executables: One with the ``--test-toolchain`` that is
passed through the test suite, and another for ``--main-toolchain`` that is
built for distribution.

``dds`` includes three toolchains that it uses to build itself in its CI
environment: ``tools/gcc-9.jsonc`` for Linux and macOS,
``tools/freebsd-gcc-9.jsonc`` for FreeBSD, and ``tools/msvc.jsonc`` for
Windows.
The default toolchains are files contained within the ``tools/`` directory of
the repository. When ``dds-ci`` builds ``dds``, it will print the path to the
toolchain file that is selected for that build.

While these toolchains will work perfectly well in CI, you may need to tweak
these for your build setup. For example: ``gcc-9.jsonc`` assumes that the GCC 9
executables are named ``gcc-9`` and ``g++-9``, which is incorrect on some
Linux distributions.
While these provided toolchains will work perfectly well in CI, you may need to
tweak these for your build setup. For example: ``gcc-9-*.jsonc`` toolchains
assume that the GCC 9 executables are named ``gcc-9`` and ``g++-9``, which is
incorrect on some Unix and Linux distributions.

It is recommended to tweak these files as necessary to get the build working on
your system. However, do not include those tweaks in a commit unless they are
necessary to get the build running in CI.

your system. However, **do not** include those tweaks in a commit unless they
are necessary to get the build running in CI.

Giving a Toolchain to ``ci.py``
===============================

Just like passing a toolchain to ``dds``, ``ci.py`` also requires a toolchain.
Simply pass the path to your desired toolchain using the ``--toolchain``/
``-T`` argument:
What's Happening?
*****************

.. code-block:: bash
The ``dds-ci`` script performs the following actions, in order:

$ python3 tools/ci.py [...] -T tools/gcc-9.jsonc
#. If given ``--clean``, remove any prior build output and downloaded
dependencies.
#. Prepare the prior version of ``dds`` that will build the current version
(usually, just download it). This is placed in ``_prebuilt/``.
#. Import the ``old-catalog.json`` into a catalog database stored within
``_prebuilt/``. This will be used to resolve the third-party packages that
``dds`` itself uses.
#. Invoke the build of ``dds`` using the prebuilt ``dds`` obtained from the
prior bootstrap phase. If ``--no-test`` or ``--rapid`` was specified, the CI
script stops here.
#. Launch ``pytest`` with the generated ``dds`` executable and start the final
release build simultaneously, and wait for both to finish.


Building for Development
************************
Unit Tests
**********

While ``ci.py`` is rigorous in maintaining a clean and reproducible environment,
we often don't need such rigor for a rapid development iteration cycle. Instead
we can invoke the build command directly in the same way that ``ci.py`` does
it:
Various pieces of ``dds`` contain unit tests. These are stored within the
``src/`` directory itself in ``*.test.cpp`` files. They are built and executed
as part of the iteration cycle *unconditionally*. These tests execute in
milliseconds so as not to burden the development iteration cycle. The more
rigorous tests are executed separately by PyTest.

.. code-block:: bash

$ _prebuilt/dds build -t [toolchain] \
--catalog _prebuilt/catalog.db \
--repo-dir _prebuilt/ci-repo
Speeding Up the Build
*********************

The ``--catalog`` and ``--repo-dir`` arguments are not strictly necessary, but
help to isolate the ``dds`` dev environment from the user-local ``dds``
environment. This is important if modifications are made to the catalog
database schema that would conflict with the one of an external ``dds``
version.
``dds``'s build is unfortunately demanding, but can be sped up by additional
tools:

.. note::
You'll likely want to run ``ci.py`` *at least once* for it to prepare the
necessary ``catalog.db``.

.. note::
As mentioned previously, if using MSVC, the above command must execute with
the appropriate VS development environment enabled.
Use the LLVM ``lld`` Linker
===========================

Installing the LLVM ``lld`` linker will *significantly* improve the time it
takes for ``dds`` and its unit test executables to link. ``dds-ci`` will
automatically recognize the presence of ``lld`` if it has been installed
properly.

Running the Test Suite
**********************
.. note::

The ``--build-only`` flag for ``ci.py`` will disable test execution. When this
flag is omitted, ``ci.py`` will execute a self-build sanity test and then
execute the main test suite, which is itself written as a set of ``pytest``
tests in the ``tests/`` subdirectory.
``dds-ci`` (and GCC) look for an executable called ``ld.ldd`` on the
executable PATH (no version suffix!). You may need to symlink the
version-suffixed executable with ``ld.ldd`` in another location on PATH so
that ``dds-ci`` (and GCC) can find it.


Unit Tests
==========
Use ``ccache``
==============

Various pieces of ``dds`` contain unit tests. These are stored within the
``src/`` directory itself in ``*.test.cpp`` files. They are built and executed
by the bootstrapped ``dds`` executable unconditionally. These tests execute
in milliseconds and do not burden the development iteration cycle.
``dds-ci`` will also recognize ``ccache`` and add it as a compiler-launcher if
it is installed on your PATH. This won't improve initial compilation times, but
can make subsequent compilations significantly faster when files are unchanged.

+ 79
- 0
docs/dev/ci-api.rst Zobrazit soubor

@@ -0,0 +1,79 @@
DDS CI Scripts Python API
#########################

Types from pytest
*****************

These types are defined by pytest, but are used extensively within the testing
scripts.

.. class:: _pytest.fixtures.FixtureRequest

.. seealso:: :class:`pytest.FixtureRequest`

.. class:: _pytest.tmpdir.TempPathFactory

.. seealso:: :class:`pytest.TempPathFactory`


Test Fixtures
*************

The following test fixtures are defined:

- :func:`~dds_ci.testing.fixtures.dds` - :class:`dds_ci.dds.DDSWrapper` - A
wrapper around the ``dds`` executable under test.
- :func:`~dds_ci.testing.fixtures.tmp_project` -
:class:`dds_ci.testing.fixtures.Project` - Create a new empty directory to be
used as a test project for ``dds`` to execute.
- :func:`~dds_ci.testing.http.http_repo` -
:class:`dds_ci.testing.http.RepoServer` - Create a new dds repository and
spawn an HTTP server to serve it.

Module: ``dds_ci``
******************

.. automodule:: dds_ci
:members:


Module: ``dds_ci.dds``
**********************

.. automodule:: dds_ci.dds
:members:


Module: ``dds_ci.proc``
***********************

.. automodule:: dds_ci.proc
:members:


Module: ``dds_ci.testing``
**************************

.. automodule:: dds_ci.testing
:members:


Module: ``dds_ci.testing.http``
*******************************

.. automodule:: dds_ci.testing.http
:members:


Module: ``dds_ci.testing.fixtures``
***********************************

.. automodule:: dds_ci.testing.fixtures
:members:


Module: ``dds_ci.testing.error``
********************************

.. automodule:: dds_ci.testing.error
:members:

+ 91
- 0
docs/dev/env.rst Zobrazit soubor

@@ -0,0 +1,91 @@
Setting Up a Build/Development Environment
##########################################

While ``dds`` is able to build itself, several aspects of build infrastructure
are controlled via Python scripts. You will need Python 3.6 or later available
on your system to get started.


.. _Poetry: python-poetry.org

Getting Started with *Poetry*
*****************************

``dds`` CI runs atop `Poetry`_, a Python project management tool. While designed
for Python projects, it serves our purposes well.


Installing Poetry
=================

If you do not have Poetry already installed, it can be obtained easily for most
any platform.
`Refer to the Poetry "Installation" documentation to learn how to get Poetry on your platform <https://python-poetry.org/docs/#installation>`_.

The remainder of this documentation will assume you are able to execute
``poetry`` on your command-line.


Setting Up the Environment
==========================

To set up the scripts and Python dependencies required for CI and development,
simply execute the following command from within the root directory of the
project::

$ poetry install

Poetry will then create a Python virtual environment that contains the Python
scripts and tools required for building and developing ``dds``.

The Python virtual environment that Poetry created can be inspected using
``poetry env info``, and can be deleted from the system using
``poetry env remove``. Refer to
`the Poetry documentation <https://python-poetry.org/docs>`_ for more
information about using Poetry.


Using the Poetry Environment
****************************

Once the ``poetry install`` command has been executed, you will now be ready to
run the ``dds`` CI scripts and tools.

The scripts are installed into the virtual environment, and need not be globally
installed anywhere else on the system. You can only access these scripts by
going through Poetry. To run any individual command within the virtual
environment, use ``poetry run``::

$ poetry run <some-command>

This will load the virtual environment, execute ``<some-command>``, then exit
the environment. This is useful for running CI scripts from outside of the
virtualenv.

**Alternatively**, the environment can be loaded persistently into a shell
session by using ``poetry shell``::

$ poetry shell

This will spawn a new interactive shell process with the virtual environment
loaded, and you can now run any CI or development script without needing to
prefix them with ``poetry run``.

Going forward, the documentation will assume you have the environment loaded
as-if by ``poetry shell``, but any ``dds``-CI-specific command can also be
executed by prefixing the command with ``poetry run``.


Working With an MSVC Environment in VSCode
==========================================

If you use Visual Studio Code as your editor and MSVC as your C++ toolchain,
you'll need to load the MSVC environment as part of your build task. ``dds`` CI
has a script designed for this purpose. To use it, first load up a shell within
the Visual C++ environment, then, from within the previously create Poetry
environment, run ``gen-msvs-vsc-task``. This program will emit a Visual Studio
Code JSON build task that builds ``dds`` and also contains the environment
variables required for the MSVC toolchain to compile and link programs. You can
save this JSON task into ``.vscode/tasks.json`` to use as your primary build
task while hacking on ``dds``.


+ 12
- 1
docs/dev/index.rst Zobrazit soubor

@@ -1,9 +1,20 @@
``dds`` Development
###################

While prebuilt ``dds`` executables are `available on the GitHub page
<releases>`_, one may wish to build ``dds`` from source.

.. _releases: https://github.com/vector-of-bool/dds/releases

The ``dds`` build process is designed to be as turn-key simple as possible.

This section will discuss how to modify and build ``dds`` itself.

.. toctree::
:maxdepth: 2

building
reqs
env
building
testing
ci-api

+ 29
- 0
docs/dev/reqs.rst Zobrazit soubor

@@ -0,0 +1,29 @@
Supported Platforms and Build Requirements
##########################################

``dds`` aims to be as cross-platform as possible. It currently build and
executes on **Windows**, **macOS**, **Linux**, and **FreeBSD**. Support for
additional platforms is possible but will require modifications to
``bootstrap.py`` that will allow it to be built on such platforms.


Build Requirements
******************

Building ``dds`` has a simple set of requirements:

- **Python 3.6** or newer to run the bootstrap/CI scripts.
- A C++ compiler that has rudimentary support for several C++20 features,
including Concepts. Newer releases of Visual C++ that ship with **VS
2019** will be sufficient on Windows, as will **GCC 9** with ``-fconcepts`` on
other platforms.

.. note::
On Windows, you will need to execute the build from within a Visual C++
enabled environment. This may involve launching the build from a Visual
Studio Command Prompt.

.. note::
At the time of writing, C++20 Concepts has not yet been released in Clang,
but should be available in LLVM/Clang 11 and newer.


+ 74
- 0
docs/dev/testing.rst Zobrazit soubor

@@ -0,0 +1,74 @@
Testing with ``pytest``
#######################

For ``dds``'s more rigorous test suite, we use the ``pytest`` testing framework.
These tests are stored in the ``tests/`` directory and written in ``test_*.py``
files.

The test suite can be run separately without ``dds-ci`` by executing ``pytest``
from within the :doc:`Poetry virtual environment <env>`::

$ pytest tests/

Note that individual tests can take between a few seconds and a few minutes to
execute, so it may be useful to execute only a subset of the tests based on the
functionality you want to test. Refer to
`the pytest documentation <https://docs.pytest.org/en/latest/>` for more
information about using and executing ``pytest``. If you are running the full
test suite, you may also want to pass the ``-n`` argument with a number of
parallel jobs to execute.


.. highlight:: python

Writing Tests
*************

If a particular aspect of ``dds`` can be tested in isolation and within a few
dozen milliseconds, you should prefer to test it as a unit test in a
``*.test.cpp`` file. The ``pytest`` tests are intended to perform full
end-to-end feature and error handling tests.

Tests are grouped into individual Python files in the ``tests/`` directory. Any
Python file containing tests must have a filename beginning with ``test_``.
Individual test functions should begin with ``test_``. All test functions should
be properly type-annotated and successfully check via ``mypy``.

The ``dds`` test suite has access to a set of test fixtures that can be used
throughout tests to perform complex setup and teardown for complete test-by-test
isolation.

Here is a simple test that simple executes ``dds`` with ``--help``::

def test_get_help(dds: DDSWrapper) -> None:
dds.run(['--help'])

In this test function, :func:`the dds object is a test fixture
<dds_ci.testing.fixtures.dds>` that wraps the ``dds`` executable under test.


Testing Error Handling
**********************

It is important that ``dds`` handle errors correctly, of course, including user
error. It is not simply enough to check that a certain operation fails: We must
be sure that it fails *correctly*. To check that the correct code path is
executed, ``dds`` can write a file containing a simple constant string
designating the error handling path that was taken. The file will be written to
the path indicated by the ``DDS_WRITE_ERROR_MARKER`` environment variable.

For examples of these error strings, search for usage of ``write_error_marker``
in the ``dds`` source code. These should only execute within error-handling
contexts, should appear near the log messages that issue diagnostics, and should
be specific to the error at hand.

To write a test that checks for a given error-handling path, use the
:func:`~dds_ci.testing.error.expect_error_marker` context manager function::

def test_sdist_invalid_project(tmp_project: Project) -> None:
# Trying to create a package archive from a project without a
# package.json5 is invalid. Check that it creates the correct
# error-message string
with error.expect_error_marker('no-package-json5'):
tmp_project.pkg_create()


+ 354
- 1
poetry.lock Zobrazit soubor

@@ -1,3 +1,11 @@
[[package]]
name = "alabaster"
version = "0.7.12"
description = "A configurable sidebar-enabled Sphinx theme"
category = "dev"
optional = false
python-versions = "*"

[[package]]
name = "apipkg"
version = "1.5"
@@ -42,6 +50,33 @@ docs = ["furo", "sphinx", "zope.interface"]
tests = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "zope.interface"]
tests_no_zope = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six"]

[[package]]
name = "babel"
version = "2.9.0"
description = "Internationalization utilities"
category = "dev"
optional = false
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"

[package.dependencies]
pytz = ">=2015.7"

[[package]]
name = "certifi"
version = "2020.12.5"
description = "Python package for providing Mozilla's CA Bundle."
category = "dev"
optional = false
python-versions = "*"

[[package]]
name = "chardet"
version = "4.0.0"
description = "Universal encoding detector for Python 2 and 3"
category = "dev"
optional = false
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*"

[[package]]
name = "colorama"
version = "0.4.4"
@@ -58,6 +93,14 @@ category = "main"
optional = false
python-versions = "*"

[[package]]
name = "docutils"
version = "0.16"
description = "Docutils -- Python Documentation Utilities"
category = "dev"
optional = false
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*"

[[package]]
name = "execnet"
version = "1.7.1"
@@ -72,6 +115,22 @@ apipkg = ">=1.4"
[package.extras]
testing = ["pre-commit"]

[[package]]
name = "idna"
version = "2.10"
description = "Internationalized Domain Names in Applications (IDNA)"
category = "dev"
optional = false
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"

[[package]]
name = "imagesize"
version = "1.2.0"
description = "Getting image size from png/jpeg/jpeg2000/gif file"
category = "dev"
optional = false
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"

[[package]]
name = "importlib-metadata"
version = "3.1.1"
@@ -108,6 +167,20 @@ pipfile_deprecated_finder = ["pipreqs", "requirementslib"]
requirements_deprecated_finder = ["pipreqs", "pip-api"]
colors = ["colorama (>=0.4.3,<0.5.0)"]

[[package]]
name = "jinja2"
version = "2.11.2"
description = "A very fast and expressive template engine."
category = "dev"
optional = false
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*"

[package.dependencies]
MarkupSafe = ">=0.23"

[package.extras]
i18n = ["Babel (>=0.8)"]

[[package]]
name = "json5"
version = "0.9.5"
@@ -127,6 +200,14 @@ category = "dev"
optional = false
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"

[[package]]
name = "markupsafe"
version = "1.1.1"
description = "Safely add untrusted strings to HTML/XML markup."
category = "dev"
optional = false
python-versions = ">=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*"

[[package]]
name = "mccabe"
version = "0.6.1"
@@ -192,6 +273,14 @@ category = "main"
optional = false
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"

[[package]]
name = "pygments"
version = "2.7.3"
description = "Pygments is a syntax highlighting package written in Python."
category = "dev"
optional = false
python-versions = ">=3.5"

[[package]]
name = "pylint"
version = "2.6.0"
@@ -281,6 +370,32 @@ pytest-forked = "*"
psutil = ["psutil (>=3.0)"]
testing = ["filelock"]

[[package]]
name = "pytz"
version = "2020.5"
description = "World timezone definitions, modern and historical"
category = "dev"
optional = false
python-versions = "*"

[[package]]
name = "requests"
version = "2.25.1"
description = "Python HTTP for Humans."
category = "dev"
optional = false
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*"

[package.dependencies]
certifi = ">=2017.4.17"
chardet = ">=3.0.2,<5"
idna = ">=2.5,<3"
urllib3 = ">=1.21.1,<1.27"

[package.extras]
security = ["pyOpenSSL (>=0.14)", "cryptography (>=1.3.4)"]
socks = ["PySocks (>=1.5.6,!=1.5.7)", "win-inet-pton"]

[[package]]
name = "rope"
version = "0.18.0"
@@ -308,6 +423,116 @@ category = "dev"
optional = false
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*"

[[package]]
name = "snowballstemmer"
version = "2.0.0"
description = "This package provides 26 stemmers for 25 languages generated from Snowball algorithms."
category = "dev"
optional = false
python-versions = "*"

[[package]]
name = "sphinx"
version = "3.4.1"
description = "Python documentation generator"
category = "dev"
optional = false
python-versions = ">=3.5"

[package.dependencies]
alabaster = ">=0.7,<0.8"
babel = ">=1.3"
colorama = {version = ">=0.3.5", markers = "sys_platform == \"win32\""}
docutils = ">=0.12"
imagesize = "*"
Jinja2 = ">=2.3"
packaging = "*"
Pygments = ">=2.0"
requests = ">=2.5.0"
snowballstemmer = ">=1.1"
sphinxcontrib-applehelp = "*"
sphinxcontrib-devhelp = "*"
sphinxcontrib-htmlhelp = "*"
sphinxcontrib-jsmath = "*"
sphinxcontrib-qthelp = "*"
sphinxcontrib-serializinghtml = "*"

[package.extras]
docs = ["sphinxcontrib-websupport"]
lint = ["flake8 (>=3.5.0)", "isort", "mypy (>=0.790)", "docutils-stubs"]
test = ["pytest", "pytest-cov", "html5lib", "cython", "typed-ast"]

[[package]]
name = "sphinxcontrib-applehelp"
version = "1.0.2"
description = "sphinxcontrib-applehelp is a sphinx extension which outputs Apple help books"
category = "dev"
optional = false
python-versions = ">=3.5"

[package.extras]
lint = ["flake8", "mypy", "docutils-stubs"]
test = ["pytest"]

[[package]]
name = "sphinxcontrib-devhelp"
version = "1.0.2"
description = "sphinxcontrib-devhelp is a sphinx extension which outputs Devhelp document."
category = "dev"
optional = false
python-versions = ">=3.5"

[package.extras]
lint = ["flake8", "mypy", "docutils-stubs"]
test = ["pytest"]

[[package]]
name = "sphinxcontrib-htmlhelp"
version = "1.0.3"
description = "sphinxcontrib-htmlhelp is a sphinx extension which renders HTML help files"
category = "dev"
optional = false
python-versions = ">=3.5"

[package.extras]
lint = ["flake8", "mypy", "docutils-stubs"]
test = ["pytest", "html5lib"]

[[package]]
name = "sphinxcontrib-jsmath"
version = "1.0.1"
description = "A sphinx extension which renders display math in HTML via JavaScript"
category = "dev"
optional = false
python-versions = ">=3.5"

[package.extras]
test = ["pytest", "flake8", "mypy"]

[[package]]
name = "sphinxcontrib-qthelp"
version = "1.0.3"
description = "sphinxcontrib-qthelp is a sphinx extension which outputs QtHelp document."
category = "dev"
optional = false
python-versions = ">=3.5"

[package.extras]
lint = ["flake8", "mypy", "docutils-stubs"]
test = ["pytest"]

[[package]]
name = "sphinxcontrib-serializinghtml"
version = "1.1.4"
description = "sphinxcontrib-serializinghtml is a sphinx extension which outputs \"serialized\" HTML files (json and pickle)."
category = "dev"
optional = false
python-versions = ">=3.5"

[package.extras]
lint = ["flake8", "mypy", "docutils-stubs"]
test = ["pytest"]

[[package]]
name = "toml"
version = "0.10.2"
@@ -332,6 +557,19 @@ category = "main"
optional = false
python-versions = "*"

[[package]]
name = "urllib3"
version = "1.26.2"
description = "HTTP library with thread-safe connection pooling, file post, and more."
category = "dev"
optional = false
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, <4"

[package.extras]
brotli = ["brotlipy (>=0.6.0)"]
secure = ["pyOpenSSL (>=0.14)", "cryptography (>=1.3.4)", "idna (>=2.0.0)", "certifi", "ipaddress"]
socks = ["PySocks (>=1.5.6,!=1.5.7,<2.0)"]

[[package]]
name = "wrapt"
version = "1.12.1"
@@ -363,9 +601,13 @@ testing = ["pytest (>=3.5,!=3.7.3)", "pytest-checkdocs (>=1.2.3)", "pytest-flake
[metadata]
lock-version = "1.1"
python-versions = "^3.6"
content-hash = "5c3cefd7d2a4b573928b14dc6291fbb7ef8a8a29306f7982ad64db4cb615e6e5"
content-hash = "d762128dfce333176ad89e2c60a91113c56efff1539f9ca1c7ab490c7ac05067"

[metadata.files]
alabaster = [
{file = "alabaster-0.7.12-py2.py3-none-any.whl", hash = "sha256:446438bdcca0e05bd45ea2de1668c1d9b032e1a9154c2c259092d77031ddd359"},
{file = "alabaster-0.7.12.tar.gz", hash = "sha256:a661d72d58e6ea8a57f7a86e37d86716863ee5e92788398526d58b26a4e4dc02"},
]
apipkg = [
{file = "apipkg-1.5-py2.py3-none-any.whl", hash = "sha256:58587dd4dc3daefad0487f6d9ae32b4542b185e1c36db6993290e7c41ca2b47c"},
{file = "apipkg-1.5.tar.gz", hash = "sha256:37228cda29411948b422fae072f57e31d3396d2ee1c9783775980ee9c9990af6"},
@@ -382,6 +624,18 @@ attrs = [
{file = "attrs-20.3.0-py2.py3-none-any.whl", hash = "sha256:31b2eced602aa8423c2aea9c76a724617ed67cf9513173fd3a4f03e3a929c7e6"},
{file = "attrs-20.3.0.tar.gz", hash = "sha256:832aa3cde19744e49938b91fea06d69ecb9e649c93ba974535d08ad92164f700"},
]
babel = [
{file = "Babel-2.9.0-py2.py3-none-any.whl", hash = "sha256:9d35c22fcc79893c3ecc85ac4a56cde1ecf3f19c540bba0922308a6c06ca6fa5"},
{file = "Babel-2.9.0.tar.gz", hash = "sha256:da031ab54472314f210b0adcff1588ee5d1d1d0ba4dbd07b94dba82bde791e05"},
]
certifi = [
{file = "certifi-2020.12.5-py2.py3-none-any.whl", hash = "sha256:719a74fb9e33b9bd44cc7f3a8d94bc35e4049deebe19ba7d8e108280cfd59830"},
{file = "certifi-2020.12.5.tar.gz", hash = "sha256:1a4995114262bffbc2413b159f2a1a480c969de6e6eb13ee966d470af86af59c"},
]
chardet = [
{file = "chardet-4.0.0-py2.py3-none-any.whl", hash = "sha256:f864054d66fd9118f2e67044ac8981a54775ec5b67aed0441892edb553d21da5"},
{file = "chardet-4.0.0.tar.gz", hash = "sha256:0d6f53a15db4120f2b08c94f11e7d93d2c911ee118b6b30a04ec3ee8310179fa"},
]
colorama = [
{file = "colorama-0.4.4-py2.py3-none-any.whl", hash = "sha256:9f47eda37229f68eee03b24b9748937c7dc3868f906e8ba69fbcbdd3bc5dc3e2"},
{file = "colorama-0.4.4.tar.gz", hash = "sha256:5941b2b48a20143d2267e95b1c2a7603ce057ee39fd88e7329b0c292aa16869b"},
@@ -390,10 +644,22 @@ distro = [
{file = "distro-1.5.0-py2.py3-none-any.whl", hash = "sha256:df74eed763e18d10d0da624258524ae80486432cd17392d9c3d96f5e83cd2799"},
{file = "distro-1.5.0.tar.gz", hash = "sha256:0e58756ae38fbd8fc3020d54badb8eae17c5b9dcbed388b17bb55b8a5928df92"},
]
docutils = [
{file = "docutils-0.16-py2.py3-none-any.whl", hash = "sha256:0c5b78adfbf7762415433f5515cd5c9e762339e23369dbe8000d84a4bf4ab3af"},
{file = "docutils-0.16.tar.gz", hash = "sha256:c2de3a60e9e7d07be26b7f2b00ca0309c207e06c100f9cc2a94931fc75a478fc"},
]
execnet = [
{file = "execnet-1.7.1-py2.py3-none-any.whl", hash = "sha256:d4efd397930c46415f62f8a31388d6be4f27a91d7550eb79bc64a756e0056547"},
{file = "execnet-1.7.1.tar.gz", hash = "sha256:cacb9df31c9680ec5f95553976c4da484d407e85e41c83cb812aa014f0eddc50"},
]
idna = [
{file = "idna-2.10-py2.py3-none-any.whl", hash = "sha256:b97d804b1e9b523befed77c48dacec60e6dcb0b5391d57af6a65a312a90648c0"},
{file = "idna-2.10.tar.gz", hash = "sha256:b307872f855b18632ce0c21c5e45be78c0ea7ae4c15c828c20788b26921eb3f6"},
]
imagesize = [
{file = "imagesize-1.2.0-py2.py3-none-any.whl", hash = "sha256:6965f19a6a2039c7d48bca7dba2473069ff854c36ae6f19d2cde309d998228a1"},
{file = "imagesize-1.2.0.tar.gz", hash = "sha256:b1f6b5a4eab1f73479a50fb79fcf729514a900c341d8503d62a62dbc4127a2b1"},
]
importlib-metadata = [
{file = "importlib_metadata-3.1.1-py3-none-any.whl", hash = "sha256:6112e21359ef8f344e7178aa5b72dc6e62b38b0d008e6d3cb212c5b84df72013"},
{file = "importlib_metadata-3.1.1.tar.gz", hash = "sha256:b0c2d3b226157ae4517d9625decf63591461c66b3a808c2666d538946519d170"},
@@ -406,6 +672,10 @@ isort = [
{file = "isort-5.6.4-py3-none-any.whl", hash = "sha256:dcab1d98b469a12a1a624ead220584391648790275560e1a43e54c5dceae65e7"},
{file = "isort-5.6.4.tar.gz", hash = "sha256:dcaeec1b5f0eca77faea2a35ab790b4f3680ff75590bfcb7145986905aab2f58"},
]
jinja2 = [
{file = "Jinja2-2.11.2-py2.py3-none-any.whl", hash = "sha256:f0a4641d3cf955324a89c04f3d94663aa4d638abe8f733ecd3582848e1c37035"},
{file = "Jinja2-2.11.2.tar.gz", hash = "sha256:89aab215427ef59c34ad58735269eb58b1a5808103067f7bb9d5836c651b3bb0"},
]
json5 = [
{file = "json5-0.9.5-py2.py3-none-any.whl", hash = "sha256:af1a1b9a2850c7f62c23fde18be4749b3599fd302f494eebf957e2ada6b9e42c"},
{file = "json5-0.9.5.tar.gz", hash = "sha256:703cfee540790576b56a92e1c6aaa6c4b0d98971dc358ead83812aa4d06bdb96"},
@@ -433,6 +703,41 @@ lazy-object-proxy = [
{file = "lazy_object_proxy-1.4.3-cp38-cp38-win32.whl", hash = "sha256:5541cada25cd173702dbd99f8e22434105456314462326f06dba3e180f203dfd"},
{file = "lazy_object_proxy-1.4.3-cp38-cp38-win_amd64.whl", hash = "sha256:59f79fef100b09564bc2df42ea2d8d21a64fdcda64979c0fa3db7bdaabaf6239"},
]
markupsafe = [
{file = "MarkupSafe-1.1.1-cp27-cp27m-macosx_10_6_intel.whl", hash = "sha256:09027a7803a62ca78792ad89403b1b7a73a01c8cb65909cd876f7fcebd79b161"},
{file = "MarkupSafe-1.1.1-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:e249096428b3ae81b08327a63a485ad0878de3fb939049038579ac0ef61e17e7"},
{file = "MarkupSafe-1.1.1-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:500d4957e52ddc3351cabf489e79c91c17f6e0899158447047588650b5e69183"},
{file = "MarkupSafe-1.1.1-cp27-cp27m-win32.whl", hash = "sha256:b2051432115498d3562c084a49bba65d97cf251f5a331c64a12ee7e04dacc51b"},
{file = "MarkupSafe-1.1.1-cp27-cp27m-win_amd64.whl", hash = "sha256:98c7086708b163d425c67c7a91bad6e466bb99d797aa64f965e9d25c12111a5e"},
{file = "MarkupSafe-1.1.1-cp27-cp27mu-manylinux1_i686.whl", hash = "sha256:cd5df75523866410809ca100dc9681e301e3c27567cf498077e8551b6d20e42f"},
{file = "MarkupSafe-1.1.1-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:43a55c2930bbc139570ac2452adf3d70cdbb3cfe5912c71cdce1c2c6bbd9c5d1"},
{file = "MarkupSafe-1.1.1-cp34-cp34m-macosx_10_6_intel.whl", hash = "sha256:1027c282dad077d0bae18be6794e6b6b8c91d58ed8a8d89a89d59693b9131db5"},
{file = "MarkupSafe-1.1.1-cp34-cp34m-manylinux1_i686.whl", hash = "sha256:62fe6c95e3ec8a7fad637b7f3d372c15ec1caa01ab47926cfdf7a75b40e0eac1"},
{file = "MarkupSafe-1.1.1-cp34-cp34m-manylinux1_x86_64.whl", hash = "sha256:88e5fcfb52ee7b911e8bb6d6aa2fd21fbecc674eadd44118a9cc3863f938e735"},
{file = "MarkupSafe-1.1.1-cp34-cp34m-win32.whl", hash = "sha256:ade5e387d2ad0d7ebf59146cc00c8044acbd863725f887353a10df825fc8ae21"},
{file = "MarkupSafe-1.1.1-cp34-cp34m-win_amd64.whl", hash = "sha256:09c4b7f37d6c648cb13f9230d847adf22f8171b1ccc4d5682398e77f40309235"},
{file = "MarkupSafe-1.1.1-cp35-cp35m-macosx_10_6_intel.whl", hash = "sha256:79855e1c5b8da654cf486b830bd42c06e8780cea587384cf6545b7d9ac013a0b"},
{file = "MarkupSafe-1.1.1-cp35-cp35m-manylinux1_i686.whl", hash = "sha256:c8716a48d94b06bb3b2524c2b77e055fb313aeb4ea620c8dd03a105574ba704f"},
{file = "MarkupSafe-1.1.1-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:7c1699dfe0cf8ff607dbdcc1e9b9af1755371f92a68f706051cc8c37d447c905"},
{file = "MarkupSafe-1.1.1-cp35-cp35m-win32.whl", hash = "sha256:6dd73240d2af64df90aa7c4e7481e23825ea70af4b4922f8ede5b9e35f78a3b1"},
{file = "MarkupSafe-1.1.1-cp35-cp35m-win_amd64.whl", hash = "sha256:9add70b36c5666a2ed02b43b335fe19002ee5235efd4b8a89bfcf9005bebac0d"},
{file = "MarkupSafe-1.1.1-cp36-cp36m-macosx_10_6_intel.whl", hash = "sha256:24982cc2533820871eba85ba648cd53d8623687ff11cbb805be4ff7b4c971aff"},
{file = "MarkupSafe-1.1.1-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:00bc623926325b26bb9605ae9eae8a215691f33cae5df11ca5424f06f2d1f473"},
{file = "MarkupSafe-1.1.1-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:717ba8fe3ae9cc0006d7c451f0bb265ee07739daf76355d06366154ee68d221e"},
{file = "MarkupSafe-1.1.1-cp36-cp36m-win32.whl", hash = "sha256:535f6fc4d397c1563d08b88e485c3496cf5784e927af890fb3c3aac7f933ec66"},
{file = "MarkupSafe-1.1.1-cp36-cp36m-win_amd64.whl", hash = "sha256:b1282f8c00509d99fef04d8ba936b156d419be841854fe901d8ae224c59f0be5"},
{file = "MarkupSafe-1.1.1-cp37-cp37m-macosx_10_6_intel.whl", hash = "sha256:8defac2f2ccd6805ebf65f5eeb132adcf2ab57aa11fdf4c0dd5169a004710e7d"},
{file = "MarkupSafe-1.1.1-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:46c99d2de99945ec5cb54f23c8cd5689f6d7177305ebff350a58ce5f8de1669e"},
{file = "MarkupSafe-1.1.1-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:ba59edeaa2fc6114428f1637ffff42da1e311e29382d81b339c1817d37ec93c6"},
{file = "MarkupSafe-1.1.1-cp37-cp37m-win32.whl", hash = "sha256:b00c1de48212e4cc9603895652c5c410df699856a2853135b3967591e4beebc2"},
{file = "MarkupSafe-1.1.1-cp37-cp37m-win_amd64.whl", hash = "sha256:9bf40443012702a1d2070043cb6291650a0841ece432556f784f004937f0f32c"},
{file = "MarkupSafe-1.1.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:6788b695d50a51edb699cb55e35487e430fa21f1ed838122d722e0ff0ac5ba15"},
{file = "MarkupSafe-1.1.1-cp38-cp38-manylinux1_i686.whl", hash = "sha256:cdb132fc825c38e1aeec2c8aa9338310d29d337bebbd7baa06889d09a60a1fa2"},
{file = "MarkupSafe-1.1.1-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:13d3144e1e340870b25e7b10b98d779608c02016d5184cfb9927a9f10c689f42"},
{file = "MarkupSafe-1.1.1-cp38-cp38-win32.whl", hash = "sha256:596510de112c685489095da617b5bcbbac7dd6384aeebeda4df6025d0256a81b"},
{file = "MarkupSafe-1.1.1-cp38-cp38-win_amd64.whl", hash = "sha256:e8313f01ba26fbbe36c7be1966a7b7424942f670f38e666995b88d012765b9be"},
{file = "MarkupSafe-1.1.1.tar.gz", hash = "sha256:29872e92839765e546828bb7754a68c418d927cd064fd4708fab9fe9c8bb116b"},
]
mccabe = [
{file = "mccabe-0.6.1-py2.py3-none-any.whl", hash = "sha256:ab8a6258860da4b6677da4bd2fe5dc2c659cff31b3ee4f7f5d64e79735b80d42"},
{file = "mccabe-0.6.1.tar.gz", hash = "sha256:dd8d182285a0fe56bace7f45b5e7d1a6ebcbf524e8f3bd87eb0f125271b8831f"},
@@ -469,6 +774,10 @@ py = [
{file = "py-1.9.0-py2.py3-none-any.whl", hash = "sha256:366389d1db726cd2fcfc79732e75410e5fe4d31db13692115529d34069a043c2"},
{file = "py-1.9.0.tar.gz", hash = "sha256:9ca6883ce56b4e8da7e79ac18787889fa5206c79dcc67fb065376cd2fe03f342"},
]
pygments = [
{file = "Pygments-2.7.3-py3-none-any.whl", hash = "sha256:f275b6c0909e5dafd2d6269a656aa90fa58ebf4a74f8fcf9053195d226b24a08"},
{file = "Pygments-2.7.3.tar.gz", hash = "sha256:ccf3acacf3782cbed4a989426012f1c535c9a90d3a7fc3f16d231b9372d2b716"},
]
pylint = [
{file = "pylint-2.6.0-py3-none-any.whl", hash = "sha256:bfe68f020f8a0fece830a22dd4d5dddb4ecc6137db04face4c3420a46a52239f"},
{file = "pylint-2.6.0.tar.gz", hash = "sha256:bb4a908c9dadbc3aac18860550e870f58e1a02c9f2c204fdf5693d73be061210"},
@@ -493,6 +802,14 @@ pytest-xdist = [
{file = "pytest-xdist-2.1.0.tar.gz", hash = "sha256:82d938f1a24186520e2d9d3a64ef7d9ac7ecdf1a0659e095d18e596b8cbd0672"},
{file = "pytest_xdist-2.1.0-py3-none-any.whl", hash = "sha256:7c629016b3bb006b88ac68e2b31551e7becf173c76b977768848e2bbed594d90"},
]
pytz = [
{file = "pytz-2020.5-py2.py3-none-any.whl", hash = "sha256:16962c5fb8db4a8f63a26646d8886e9d769b6c511543557bc84e9569fb9a9cb4"},
{file = "pytz-2020.5.tar.gz", hash = "sha256:180befebb1927b16f6b57101720075a984c019ac16b1b7575673bea42c6c3da5"},
]
requests = [
{file = "requests-2.25.1-py2.py3-none-any.whl", hash = "sha256:c210084e36a42ae6b9219e00e48287def368a26d03a048ddad7bfee44f75871e"},
{file = "requests-2.25.1.tar.gz", hash = "sha256:27973dd4a904a4f13b263a19c866c13b92a39ed1c964655f025f3f8d3d75b804"},
]
rope = [
{file = "rope-0.18.0.tar.gz", hash = "sha256:786b5c38c530d4846aa68a42604f61b4e69a493390e3ca11b88df0fbfdc3ed04"},
]
@@ -504,6 +821,38 @@ six = [
{file = "six-1.15.0-py2.py3-none-any.whl", hash = "sha256:8b74bedcbbbaca38ff6d7491d76f2b06b3592611af620f8426e82dddb04a5ced"},
{file = "six-1.15.0.tar.gz", hash = "sha256:30639c035cdb23534cd4aa2dd52c3bf48f06e5f4a941509c8bafd8ce11080259"},
]
snowballstemmer = [
{file = "snowballstemmer-2.0.0-py2.py3-none-any.whl", hash = "sha256:209f257d7533fdb3cb73bdbd24f436239ca3b2fa67d56f6ff88e86be08cc5ef0"},
{file = "snowballstemmer-2.0.0.tar.gz", hash = "sha256:df3bac3df4c2c01363f3dd2cfa78cce2840a79b9f1c2d2de9ce8d31683992f52"},
]
sphinx = [
{file = "Sphinx-3.4.1-py3-none-any.whl", hash = "sha256:aeef652b14629431c82d3fe994ce39ead65b3fe87cf41b9a3714168ff8b83376"},
{file = "Sphinx-3.4.1.tar.gz", hash = "sha256:e450cb205ff8924611085183bf1353da26802ae73d9251a8fcdf220a8f8712ef"},
]
sphinxcontrib-applehelp = [
{file = "sphinxcontrib-applehelp-1.0.2.tar.gz", hash = "sha256:a072735ec80e7675e3f432fcae8610ecf509c5f1869d17e2eecff44389cdbc58"},
{file = "sphinxcontrib_applehelp-1.0.2-py2.py3-none-any.whl", hash = "sha256:806111e5e962be97c29ec4c1e7fe277bfd19e9652fb1a4392105b43e01af885a"},
]
sphinxcontrib-devhelp = [
{file = "sphinxcontrib-devhelp-1.0.2.tar.gz", hash = "sha256:ff7f1afa7b9642e7060379360a67e9c41e8f3121f2ce9164266f61b9f4b338e4"},
{file = "sphinxcontrib_devhelp-1.0.2-py2.py3-none-any.whl", hash = "sha256:8165223f9a335cc1af7ffe1ed31d2871f325254c0423bc0c4c7cd1c1e4734a2e"},
]
sphinxcontrib-htmlhelp = [
{file = "sphinxcontrib-htmlhelp-1.0.3.tar.gz", hash = "sha256:e8f5bb7e31b2dbb25b9cc435c8ab7a79787ebf7f906155729338f3156d93659b"},
{file = "sphinxcontrib_htmlhelp-1.0.3-py2.py3-none-any.whl", hash = "sha256:3c0bc24a2c41e340ac37c85ced6dafc879ab485c095b1d65d2461ac2f7cca86f"},
]
sphinxcontrib-jsmath = [
{file = "sphinxcontrib-jsmath-1.0.1.tar.gz", hash = "sha256:a9925e4a4587247ed2191a22df5f6970656cb8ca2bd6284309578f2153e0c4b8"},
{file = "sphinxcontrib_jsmath-1.0.1-py2.py3-none-any.whl", hash = "sha256:2ec2eaebfb78f3f2078e73666b1415417a116cc848b72e5172e596c871103178"},
]
sphinxcontrib-qthelp = [
{file = "sphinxcontrib-qthelp-1.0.3.tar.gz", hash = "sha256:4c33767ee058b70dba89a6fc5c1892c0d57a54be67ddd3e7875a18d14cba5a72"},
{file = "sphinxcontrib_qthelp-1.0.3-py2.py3-none-any.whl", hash = "sha256:bd9fc24bcb748a8d51fd4ecaade681350aa63009a347a8c14e637895444dfab6"},
]
sphinxcontrib-serializinghtml = [
{file = "sphinxcontrib-serializinghtml-1.1.4.tar.gz", hash = "sha256:eaa0eccc86e982a9b939b2b82d12cc5d013385ba5eadcc7e4fed23f4405f77bc"},
{file = "sphinxcontrib_serializinghtml-1.1.4-py2.py3-none-any.whl", hash = "sha256:f242a81d423f59617a8e5cf16f5d4d74e28ee9a66f9e5b637a18082991db5a9a"},
]
toml = [
{file = "toml-0.10.2-py2.py3-none-any.whl", hash = "sha256:806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b"},
{file = "toml-0.10.2.tar.gz", hash = "sha256:b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f"},
@@ -536,6 +885,10 @@ typing-extensions = [
{file = "typing_extensions-3.7.4.3-py3-none-any.whl", hash = "sha256:7cb407020f00f7bfc3cb3e7881628838e69d8f3fcab2f64742a5e76b2f841918"},
{file = "typing_extensions-3.7.4.3.tar.gz", hash = "sha256:99d4073b617d30288f569d3f13d2bd7548c3a7e4c8de87db09a9d29bb3a4a60c"},
]
urllib3 = [
{file = "urllib3-1.26.2-py2.py3-none-any.whl", hash = "sha256:d8ff90d979214d7b4f8ce956e80f4028fc6860e4431f731ea4a8c08f23f99473"},
{file = "urllib3-1.26.2.tar.gz", hash = "sha256:19188f96923873c92ccb987120ec4acaa12f0461fa9ce5d3d0772bc965a39e08"},
]
wrapt = [
{file = "wrapt-1.12.1.tar.gz", hash = "sha256:b62ffa81fb85f4332a4f609cab4ac40709470da05643a082ec1eb88e6d9b97d7"},
]

+ 1
- 0
pyproject.toml Zobrazit soubor

@@ -25,6 +25,7 @@ pylint = "^2.6.0"
mypy = "^0.790"
rope = "^0.18.0"
yapf = "^0.30.0"
Sphinx = "^3.4.1"

[tool.poetry.scripts]
dds-ci = "dds_ci.main:start"

+ 2
- 0
tools/dds_ci/main.py Zobrazit soubor

@@ -79,6 +79,7 @@ def test_build(dds: DDSWrapper, args: CommandArguments) -> DDSWrapper:
to build the new dds. Returns a DDSWrapper around the generated test executable.
"""
test_tc = args.test_toolchain or toolchain.get_default_audit_toolchain()
print(f'Test build is building with toolchain: {test_tc}')
build_dir = paths.BUILD_DIR
with toolchain.fixup_toolchain(test_tc) as new_tc:
dds.build(toolchain=new_tc, root=paths.PROJECT_ROOT, build_root=build_dir, jobs=args.jobs, timeout=60 * 15)
@@ -111,6 +112,7 @@ def main_build(dds: DDSWrapper, args: CommandArguments) -> int:
main_tc = args.toolchain or (
# If we are in rapid-dev mode, use the test toolchain, which had audit/debug enabled
toolchain.get_default_toolchain() if not args.rapid else toolchain.get_default_audit_toolchain())
print(f'Building with toolchain: {main_tc}')
with toolchain.fixup_toolchain(main_tc) as new_tc:
try:
dds.build(toolchain=new_tc,

+ 12
- 1
tools/dds_ci/testing/error.py Zobrazit soubor

@@ -1,5 +1,5 @@
"""
Test utility for error checking
Test utilities for error checking
"""

from contextlib import contextmanager
@@ -12,6 +12,17 @@ import os

@contextmanager
def expect_error_marker(expect: str) -> Iterator[None]:
"""
A context-manager function that should wrap a scope that causes an error
from ``dds``.

:param expect: The error message ID string that is expected to appear.

The wrapped scope should raise :class:`subprocess.CalledProcessError`.

After handling the exception, asserts that the subprocess wrote an
error marker containing the string given in ``expect``.
"""
tdir = Path(tempfile.mkdtemp())
err_file = tdir / 'error'
try:

+ 42
- 1
tools/dds_ci/testing/fixtures.py Zobrazit soubor

@@ -49,13 +49,19 @@ class LibraryJSON(_LibraryJSONRequired, total=False):


class Project:
"""
Utilities to access a project being used as a test.
"""
def __init__(self, dirpath: Path, dds: DDSWrapper) -> None:
self.dds = dds
self.dds = dds.clone()
self.root = dirpath
self.build_root = dirpath / '_build'

@property
def package_json(self) -> PackageJSON:
"""
Get/set the content of the `package.json` file for the project.
"""
return cast(PackageJSON, json.loads(self.root.joinpath('package.jsonc').read_text()))

@package_json.setter
@@ -64,6 +70,9 @@ class Project:

@property
def library_json(self) -> LibraryJSON:
"""
Get/set the content of the `library.json` file for the project.
"""
return cast(LibraryJSON, json.loads(self.root.joinpath('library.jsonc').read_text()))

@library_json.setter
@@ -108,6 +117,10 @@ class Project:
self.dds.run(['sdist', 'export', self.dds.cache_dir_arg, self.project_dir_arg])

def write(self, path: Pathish, content: str) -> Path:
"""
Write the given `content` to `path`. If `path` is relative, it will
be resolved relative to the root directory of this project.
"""
path = Path(path)
if not path.is_absolute():
path = self.root / path
@@ -118,10 +131,17 @@ class Project:

@pytest.fixture()
def test_parent_dir(request: FixtureRequest) -> Path:
"""
:class:`pathlib.Path` fixture pointing to the parent directory of the file
containing the test that is requesting the current fixture
"""
return Path(request.fspath).parent


class ProjectOpener():
"""
A test fixture that opens project directories for testing
"""
def __init__(self, dds: DDSWrapper, request: FixtureRequest, worker: str,
tmp_path_factory: TempPathFactory) -> None:
self.dds = dds
@@ -140,6 +160,14 @@ class ProjectOpener():
return Path(self._request.fspath).parent

def open(self, dirpath: Pathish) -> Project:
"""
Open a new project testing fixture from the given project directory.

:param dirpath: The directory that contains the project to use.

Clones the given directory and then opens a project within that clone.
The clone directory will be destroyed when the test fixture is torn down.
"""
dirpath = Path(dirpath)
if not dirpath.is_absolute():
dirpath = self.test_dir / dirpath
@@ -168,6 +196,11 @@ class ProjectOpener():
@pytest.fixture()
def project_opener(request: FixtureRequest, worker_id: str, dds: DDSWrapper,
tmp_path_factory: TempPathFactory) -> ProjectOpener:
"""
A fixture factory that can open directories as Project objects for building
and testing. Duplicates the project directory into a temporary location so
that the original test directory remains unchanged.
"""
opener = ProjectOpener(dds, request, worker_id, tmp_path_factory)
return opener

@@ -175,6 +208,10 @@ def project_opener(request: FixtureRequest, worker_id: str, dds: DDSWrapper,
@pytest.fixture()
def tmp_project(request: FixtureRequest, worker_id: str, project_opener: ProjectOpener,
tmp_path_factory: TempPathFactory) -> Project:
"""
A fixture that generates an empty temporary project directory that will be thrown away
when the test completes.
"""
if worker_id != 'master':
proj_dir = tmp_path_factory.mktemp('temp-project')
return project_opener.open(proj_dir)
@@ -189,11 +226,15 @@ def tmp_project(request: FixtureRequest, worker_id: str, project_opener: Project

@pytest.fixture(scope='session')
def dds(dds_exe: Path) -> NewDDSWrapper:
"""
A :class:`~dds_ci.dds.DDSWrapper` around the dds executable under test
"""
wr = NewDDSWrapper(dds_exe)
return wr


@pytest.fixture(scope='session')
def dds_exe(pytestconfig: PyTestConfig) -> Path:
"""A :class:`pathlib.Path` pointing to the DDS executable under test"""
opt = pytestconfig.getoption('--dds-exe') or paths.BUILD_DIR / 'dds'
return Path(opt)

Načítá se…
Zrušit
Uložit