diff --git a/.github/workflows/ci-build.yml b/.github/workflows/ci-build.yml index 047dd30d..8883275a 100644 --- a/.github/workflows/ci-build.yml +++ b/.github/workflows/ci-build.yml @@ -38,6 +38,12 @@ jobs: with: python-version: 3.8 + - name: Install diagrams dependencies + run: | + sudo apt-get update + sudo apt-get install nodejs npm graphviz + npm install -g @mermaid-js/mermaid-cli + - name: Build HTML Documentation run: | pip install -r requirements_docs.txt --disable-pip-version-check diff --git a/.github/workflows/nightly-build.yml b/.github/workflows/nightly-build.yml index 1cfeab2f..4c190bc0 100644 --- a/.github/workflows/nightly-build.yml +++ b/.github/workflows/nightly-build.yml @@ -18,6 +18,12 @@ jobs: with: python-version: 3.8 + - name: Install mermaid-cli + run: | + sudo apt-get update + sudo apt-get install nodejs npm graphviz + npm install -g @mermaid-js/mermaid-cli + - name: Build HTML Documentation run: | pip install -r requirements_docs.txt --disable-pip-version-check diff --git a/doc/source/conf.py b/doc/source/conf.py index 05734417..39ac25a1 100755 --- a/doc/source/conf.py +++ b/doc/source/conf.py @@ -30,10 +30,14 @@ "sphinx_copybutton", "sphinx_toolbox.collapse", "sphinx.ext.autodoc", + "sphinx.ext.autosectionlabel", "sphinx.ext.autosummary", + "sphinx.ext.graphviz", "sphinx.ext.intersphinx", "sphinx.ext.napoleon", "sphinx.ext.todo", + "sphinxcontrib.mermaid", + "sphinx_tabs.tabs", ] # Intersphinx mapping @@ -89,3 +93,22 @@ # author, 'ansys.mapdl.core', 'Pythonic interface to MAPDL using gRPC', # 'Engineering Software'), # ] + +# Include numerical references for the figures +numfig = True + +# Do not include the following patterns as documentation source files. +# See issue: https://github.com/sphinx-doc/sphinx/issues/1668 +exclude_patterns = ["packaging/diag/*", "packaging/code/*"] + +# Fix excessive margins in mermaid output. +# See issue: https://github.com/mermaid-js/mermaid/issues/1800#issuecomment-741617143 +mermaid_output_format = "png" +mermaid_params = ["--width", "2000"] + +# Generate section labels up to three levels deep +autosectionlabel_maxdepth = 3 + +# TODO: warning suppression is temporary till https://github.com/pyansys/dev-guide/issues/64 +# gets fully implemented. +suppress_warnings = ["autosectionlabel.*"] diff --git a/doc/source/guidelines/dev_practices.rst b/doc/source/guidelines/dev_practices.rst index 10a714f5..be30796b 100644 --- a/doc/source/guidelines/dev_practices.rst +++ b/doc/source/guidelines/dev_practices.rst @@ -84,11 +84,11 @@ changes any given branch is introducing before looking at the code. - ``doc/``: for any changes only pertaining to documentation - ``no-ci/``: for low-impact activity that should not trigger the CI routines -- ``testing/``: improvements or changes to testing -- ``release/``: releases (see below) +- ``testing/``: Improvements or changes to testing +- ``release/``: Releases (see below) -Testing -~~~~~~~ +Unit Testing +~~~~~~~~~~~~ When making changes, periodically test locally before creating a PR. Because the following tests are executed after any commit or PR, we ask that you perform the following procedure locally to track down diff --git a/doc/source/guidelines/test_practices.rst b/doc/source/guidelines/test_practices.rst index 73a95ec0..378dba6e 100644 --- a/doc/source/guidelines/test_practices.rst +++ b/doc/source/guidelines/test_practices.rst @@ -245,6 +245,8 @@ and a Python wrapping: command : str Command to run on the remote server. + """ + Your example test would be: .. code:: python @@ -282,8 +284,8 @@ Testing Framework For consistency, PyAnsys tools and libraries should use either the `unittest `_ or `pytest `_ frameworks for unit testing. As described in -:ref:`repo_dir_struct`, unit tests should be placed into the ``tests`` -directory in the root directory of the library:: +:ref:`Required Files for a PyAnsys Project`, unit tests should be placed in the ``tests`` +directory in the library's root directory:: tests/ test_basic.py diff --git a/doc/source/overview/administration.rst b/doc/source/overview/administration.rst index d6afd25e..9371875a 100644 --- a/doc/source/overview/administration.rst +++ b/doc/source/overview/administration.rst @@ -43,14 +43,14 @@ description, or branch protection management. Each repository is expected to follow this minimum set of standards: -- Minimum code standards following PEP8. See :ref:`best_practices`. -- CI/CD using GitHub actions or Azure DevOps to enforce coding standards. See :ref:`ci_cd`. -- Publicly hosted documentation detailing API with examples. See +- PEP8 code standards. See :ref:`best_practices`. +- CI/CD using GitHub actions or Azure DevOps to enforce coding standards. +- Publicly hosted documentation describing the API and providing examples. See :ref:`api_documentation`. -- Unit testing with at least 80% test coverage. See :ref:`ci_cd`. +- Unit testing with at least 80% test coverage. - Infrastructure in place to deploy the library as a package on `PyPi - `_. See :ref:`packaging`. -- Proper license file and author. See :ref:`setup_file` and :ref:`license_file`. + `_. See :ref:`Packaging Style`. +- Proper license file and author. See :ref:`The \`\`setup.py\`\` File` and :ref:`The \`\`LICENSE\`\` File`. Release Procedures and Versioning diff --git a/doc/source/overview/contributing.rst b/doc/source/overview/contributing.rst index 23aa5933..62174bcc 100644 --- a/doc/source/overview/contributing.rst +++ b/doc/source/overview/contributing.rst @@ -136,7 +136,7 @@ to :ref:`branch_naming` when you are ready to create a pull request. Licensing ========= All contributed code will be licensed under the MIT License. For more information, see -:ref:`license_file`. The ``LICENSE`` file containing the MIT License must be included in +:ref:`The \`\`LICENSE\`\` File`. The ``LICENSE`` file containing the MIT License must be included in the root directory of a PyAnsys repository. If you did not write the code that you are contributing yourself, it is your diff --git a/doc/source/packaging/build-systems.rst b/doc/source/packaging/build-systems.rst new file mode 100644 index 00000000..6018d39d --- /dev/null +++ b/doc/source/packaging/build-systems.rst @@ -0,0 +1,173 @@ +.. _ref_build_system: + +############ +Build System +############ + +The build system is a fundamental tool for packaging Python +libraries. It generates distribution files that can be shared with +users and developers. + + +Artifacts +========= + +The build system allows maintainers to generate artifacts for their Python +libraries. Here, `artifacts` refers to both wheel and source files: + +- ``Wheel files`` have the ``*.whl`` file extension. +- ``Source files`` have the ``*.tar.gz`` or ``*.zip`` extension. + +These are the files to upload to `PyPI`_ when releasing a new version of a +PyAnsys project. + +.. warning:: + + Not all files are included by default in the source distribution. A + `MANIFEST.in`_ is required at the root of the project to specify additional + files. + + +The interaction between the maintainer and the build system is performed using a +build system tool. This tool provides both a frontend and a backend. The maintainers +trigger the frontend, which then calls the backend to read the +project directory and generate the artifacts, as :numref:`build system diag` shows. + +.. include:: diag/build_system_diag.rst + + +PEP 517 and PEP 518 +=================== + +For a long time, the ``setup.py`` file was the only way of specifying the +project structure, metadata, and installation workflow that `pip`_ was to follow. +However, having to execute a Python file when installing a Python package +introduced the following problems: + +- It was not possible to know which dependencies required the ``setup.py`` file + to be properly executed. + +- The default Python package installer, `pip`_, expected `setuptools`_ to be the + default build system tool, excluding others like `flit`_ and `poetry`_. + +These problems led to the acceptance of `PEP 517`_ and `PEP 518`_. + + +PEP 517 +------- + +`PEP 517`_ allows Python developers to specify the build backend tool for +generating artifacts. The previous :numref:`build system diag` diagram shows the +most popular backends: + +- `Setuptools`_ , while very popular, lacks the ability to declare build time dependencies + and is difficult to extend. +- `Flit`_ is a lightweight build system tool for Python. +- `Poetry`_ focuses on dependency management and environment isolation. + +`PEP 517` introduced the ``build-backend`` key inside the +``[build-system]`` table in the ``pyproject.toml``. + + +PEP 518 +------- + +In addition to the ``setup.py`` file, `PEP 518`_ includes a project file named +``pyproject.toml``. The main goal of this file is to specify build time dependencies. +However, some build system tools like `flit`_ or `poetry`_ are able to specify all +project metadata inside the ``pyproject.toml`` file and eliminate usage of the +``setup.py`` file. + +To specify the build time requirements, the ``[build-system]`` table must be +declared in the ``pyproject.toml`` file. Within it, the ``requires`` key is +assigned to a list of strings, which are the build time +requirements. + +The combination of `PEP 517`_ and `PEP 518`_ leads to the following syntax in a +``pyproject.toml`` file: + +.. code:: toml + + [build-system] + requires = ["flit"] # Defined by PEP 518 + build-backend = "flit_core.api" # Defined by PEP 517 + + +Build Backend Tools +=================== + +This section lists some of the most popular build systems in the +Python ecosystem. Although all of them achieve the same goal, there are a few +differences regarding their capabilities and the way of specifying project +metadata. + +.. TODO: Include links to each build system allowed metadata fields + +Setuptools +---------- + +`Setuptools`_ has been a part of the Python ecosystem for a long time. Unless +you require high control over your project's installation steps, you should use +`flit`_ or `poetry`_. + +If you do not need a dynamic installation process, you can consider using a +``setup.cfg`` file. However, the ``setup.py`` file is still required. The ``setup.cfg`` file +should have a call to the ``setup()`` function to act as the entry point of the +build backend system. + +All of these `setuptools metadata fields`_ are valid and must be +specified either in the ``setup.py`` or ``setup.cfg`` file. + + +Flit +---- + +Flit is a modern and lightweight build system that requires developers +to manage virtual environments on their own. Developers must: + +* Create a virtual environment and activate it. +* Install the package in editable mode. + +Flit is the default tool for creating a new ``pyansys`` project when using the +`ansys-templates tool`_. + +The ``[project]`` section specifies the project's metadata and required +dependencies. For more information, see `flit pyproject.toml +guidelines`_. + + +Poetry +------ + +Because of its ``poetry.lock`` file, Poetry provides strong dependency pinning. When +installing a package, poetry creates a virtual environment, thus ensuring an isolated +package development environment. + +Nevertheless, it is possible to make Poetry ignore the ``poetry.lock`` file with: + +.. code:: bash + + poetry config virtualenvs.create false --local + +Using `poetry`_ is popular because it: + +* Supports pinning dependency versions via a ``poetry.lock`` file that can be + used for testing and CI +* Allows downstream packages to still consume a loose dependency specification +* Integrates with `dependabot`_ to update the pinned version + +The ``[tool.poetry]`` section contains metadata and defines project +dependencies. For more information, see `poetry pyproject.toml documentation`_. + + +.. _MANIFEST.in: https://packaging.python.org/en/latest/guides/using-manifest-in/ +.. _PyPI: https://pypi.org/ +.. _pip: +.. _flit: https://flit.pypa.io/en/latest/ +.. _poetry: https://python-poetry.org/ +.. _poetry pyproject.toml documentation: https://python-poetry.org/docs/pyproject/ +.. _setuptools: https://pypi.org/project/setuptools/ +.. _setuptools metadata fields: https://setuptools.pypa.io/en/latest/userguide/declarative_config.html#declarative-config +.. _flit pyproject.toml guidelines: https://flit.readthedocs.io/en/latest/pyproject_toml.html +.. _dependabot: https://docs.github.com/en/code-security/supply-chain-security/keeping-your-dependencies-updated-automatically/about-dependabot-version-updates +.. _ansys-templates tool: https://github.com/pyansys/ansys-templates diff --git a/doc/source/packaging/ci_cd.rst b/doc/source/packaging/ci_cd.rst deleted file mode 100644 index 7bda3be1..00000000 --- a/doc/source/packaging/ci_cd.rst +++ /dev/null @@ -1,24 +0,0 @@ -.. _ci_cd: - -CI/CD -##### -CI/CD (continuous integration/continuous delivery) is -achieved using either public `Azure DevOps -`_ or -`GitHub Workflow Actions `_ -for unit testing, release builds, and documentation builds. -The selected method should also be used for branch protection. -For more information, see :ref:`repository_management`. - -Here are some good examples: - -- `PyAnsys Sphinx documentation theme action `_: - Generates Ansys Python package documentation using the `PyAnsys Sphinx theme `__. -- `MAPDL documentation action `_: - Generates MAPDL documentation using product containers. -- `PyAEDT unit testing action `_: - Runs unit testing using an application preinstalled on a self-hosted agent. -- `MAPDL Azure DevOps action `_: - Uses a containerized application to run unit testing for an Azure pipeline. -- `DPF-Core Azure DevOps action `_: - Uses a universal package to run unit testing. diff --git a/doc/source/packaging/code/license_mit_code.rst b/doc/source/packaging/code/license_mit_code.rst new file mode 100644 index 00000000..45a5e404 --- /dev/null +++ b/doc/source/packaging/code/license_mit_code.rst @@ -0,0 +1,23 @@ +.. code:: text + + MIT License + + Copyright (c) ANSYS, Inc. All rights reserved. + + 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/doc/source/packaging/code/pyproject_code.rst b/doc/source/packaging/code/pyproject_code.rst new file mode 100644 index 00000000..de96bc9d --- /dev/null +++ b/doc/source/packaging/code/pyproject_code.rst @@ -0,0 +1,13 @@ +.. tabs:: + + .. tab:: setuptools + + .. include:: code/pyproject_setuptools_code.rst + + .. tab:: flit + + .. include:: code/pyproject_flit_code.rst + + .. tab:: poetry + + .. include:: code/pyproject_poetry_code.rst diff --git a/doc/source/packaging/code/pyproject_flit_code.rst b/doc/source/packaging/code/pyproject_flit_code.rst new file mode 100644 index 00000000..8ad238d7 --- /dev/null +++ b/doc/source/packaging/code/pyproject_flit_code.rst @@ -0,0 +1,36 @@ +.. code:: toml + + [build-system] + requires = ["flit_core >=3.2,<4"] + build-backend = "flit_core.buildapi" + + [project] + # Check https://flit.readthedocs.io/en/latest/pyproject_toml.html for all available sections + name = "Py " + version = "" + description = "A Python wrapper for Ansys " + readme = "README.rst" + requires-python = ">=3." + license = {file = "LICENSE"} + authors = [ + {name = "ANSYS, Inc.", email = "pyansys.support@ansys.com"}, + ] + maintainers = [ + {name = "PyAnsys developers", email = "pyansys.maintainers@ansys.com"}, + ] + + classifiers = [ + "Development Status :: 4 - Beta", + "Programming Language :: Python :: 3", + "License :: OSI Approved :: MIT License", + "Operating System :: OS Independent", + ] + dependencies = [ + "importlib-metadata >=4.0", + ] + + [tool.flit.module] + name = "ansys.." + + [project.urls] + Source = "https://github.com/pyansys/py-" diff --git a/doc/source/packaging/code/pyproject_poetry_code.rst b/doc/source/packaging/code/pyproject_poetry_code.rst new file mode 100644 index 00000000..0cc417eb --- /dev/null +++ b/doc/source/packaging/code/pyproject_poetry_code.rst @@ -0,0 +1,29 @@ +.. code:: toml + + [build-system] + requires = ["poetry-core>=1.0.0"] + build-backend = "poetry.core.masonry.api" + + [tool.poetry] + # Check https://python-poetry.org/docs/pyproject/ for all available sections + name = "Py " + version = "" + description = "A Python wrapper for " + license = "MIT" + authors = ["ANSYS, Inc. "] + maintainers = ["PyAnsys developers "] + readme = "README.rst" + repository = "https://github.com/pyansys/py-" + classifiers = [ + "Development Status :: 4 - Beta", + "Programming Language :: Python :: 3", + "License :: OSI Approved :: MIT License", + "Operating System :: OS Independent", + ] + packages = [ + { include = "ansys", from = "src" }, + ] + + [tool.poetry.dependencies] + python = ">=3." + importlib-metadata = {version = "^4.0", python = "<3.8"} diff --git a/doc/source/packaging/code/pyproject_setuptools_code.rst b/doc/source/packaging/code/pyproject_setuptools_code.rst new file mode 100644 index 00000000..f3b445dd --- /dev/null +++ b/doc/source/packaging/code/pyproject_setuptools_code.rst @@ -0,0 +1,5 @@ +.. code:: toml + + [build-system] + requires = ["setuptools", "wheel"] + build-backend = "setuptools.build_meta" diff --git a/doc/source/packaging/code/setup_file_code.rst b/doc/source/packaging/code/setup_file_code.rst new file mode 100644 index 00000000..3fce14eb --- /dev/null +++ b/doc/source/packaging/code/setup_file_code.rst @@ -0,0 +1,28 @@ +.. code:: python + + """Installation file for ansys-mapdl-core.""" + + from setuptools import find_namespace_packages, setup + + setup( + name="ansys--", + packages=find_namespace_packages(where="src", include="ansys*"), + package_dir={"": "src"}, + version="", + description="", + long_description=open("README.rst").read(), + license="MIT", + author="ANSYS, Inc.", + author_email="pyansys.support@ansys.com", + maintainer="PyAnsys developers", + maintainer_email="pyansys.maintainers@ansys.com", + classifiers=[ + "Development Status :: 4 - Beta", + "Programming Language :: Python :: 3", + "License :: OSI Approved :: MIT License", + "Operating System :: OS Independent", + ], + url="-", + python_requires=">=3.", + install_requires=["importlib-metadata >=4.0"], + ) diff --git a/doc/source/images/diagram.png b/doc/source/packaging/diag/architecture_diag.png similarity index 100% rename from doc/source/images/diagram.png rename to doc/source/packaging/diag/architecture_diag.png diff --git a/doc/source/packaging/diag/build_system_diag.rst b/doc/source/packaging/diag/build_system_diag.rst new file mode 100644 index 00000000..11e7d2e4 --- /dev/null +++ b/doc/source/packaging/diag/build_system_diag.rst @@ -0,0 +1,62 @@ +.. _build system diag: +.. mermaid:: + :caption: Maintainers use the build system to create artifacts. + :alt: Maintainers use the build system to create artifacts. + :align: center + + flowchart LR + + maintainers[Maintainers] + + subgraph build_system[Build System] + direction LR + + subgraph frontend[Frontend] + direction LR + setuptools + flit + poetry + end + + subgraph backend[Backend] + direction LR + setuptools.build_back + flit_core.api + poetry.masonry.api + end + + subgraph project[PyAnsys Project] + direction LR + sourcefiles[src/ tests/ doc/ ] + metadata[LICENSE README.rst] + pyproject.toml + end + + end + + subgraph artifacts[Artifacts] + direction LR + *.whl + *.tar.gz + end + + %% Link definitions + maintainers-- Trigger -->build_system + frontend-- Calls -->backend + project-- Read by -->backend + build_system-- Generates -->artifacts + *.whl --- *.tar.gz + + %% Define style for nodes and subgraphs + classDef NodeStyle stroke:black,color:black,fill:white,stroke-width:2px,font-size:12pt; + classDef SubgraphStyle stroke:black,color:black,fill:white,stroke-width:2px,font-size:14pt,font-weight:bold; + + %% Assign style to elements + class maintainers,setuptools,flit,poetry,setuptools.build_back,flit_core.api,poetry.masonry.api,pyproject.toml,metadata,sourcefiles,*.whl,*.tar.gz NodeStyle; + class build_system,frontend,backend,project,artifacts SubgraphStyle; + + %% HACK: add some space between subgraph header and its nodes + linkStyle 1 fill:none,color:black,stroke:black; + linkStyle 2 fill:none,color:black,stroke:black; + linkStyle 3 fill:none,color:black,stroke:black; + linkStyle 4 fill:none,color:white,stroke:white; diff --git a/doc/source/packaging/diag/doc_structure_diag.rst b/doc/source/packaging/diag/doc_structure_diag.rst new file mode 100644 index 00000000..1235cada --- /dev/null +++ b/doc/source/packaging/diag/doc_structure_diag.rst @@ -0,0 +1,59 @@ +.. _doc structure diag: +.. graphviz:: + :caption: Generic structure for the ``doc/`` directory. + :alt: Generic structure for the ``doc/`` directory. + :align: center + + digraph "sphinx-ext-graphviz" { + size="8,6"; + rankdir="LR"; + graph [ + fontname="Verdana", fontsize="10", color="black", fillcolor="white" + ]; + node [ + fontname="Verdana", fontsize="10", style="filled", color="black", fillcolor="#ffc107" + ]; + + doc [ + label="doc", shape="folder" + ]; + + build [ + label="_build", shape="folder" + ] + + source [ + label="source", shape="folder" + ] + + make_bat [ + label="make.bat", shape="file" + ] + + makefile [ + label="Makefile", shape="file" + ] + + static [ + label="_static", shape="folder" + ] + + index_file [ + label="index.rst", shape="file" + ] + + conf_file [ + label="conf.py", shape="file" + ] + + doc -> build; + doc -> source; + doc -> make_bat; + doc -> makefile; + + source -> conf_file; + source -> index_file; + source -> static; + } + + diff --git a/doc/source/packaging/diag/grpc_structure_diag.rst b/doc/source/packaging/diag/grpc_structure_diag.rst new file mode 100644 index 00000000..65058aa0 --- /dev/null +++ b/doc/source/packaging/diag/grpc_structure_diag.rst @@ -0,0 +1,55 @@ +.. _grpc structure diag: +.. graphviz:: + :caption: Namespace convention for gRPC interface packages. + :alt: Namespace convention for gRPC interface packages. + :align: center + + digraph "sphinx-ext-graphviz" { + size="8,6"; + rankdir="LR"; + graph [ + fontname="Verdana", fontsize="10", color="black", fillcolor="white",splines=ortho + ]; + node [ + fontname="Verdana", fontsize="10", style="filled", color="black", fillcolor="#ffc107" + ]; + + ansys_api_product [ + label="ansys-api-product", shape="folder" + ]; + + ansys [ + label="ansys", shape="folder" + ]; + + api [ + label="api", shape="folder" + ]; + + product [ + label="product", shape="folder" + ]; + + version [ + label="VERSION", shape="file" + ]; + + v0 [ + label="v0", shape="folder" + ]; + + proto_files [ + label="\*.proto", shape="file" + ]; + + + ansys_api_product -> ansys; + ansys -> api; + api -> product; + product -> version; + product -> v0; + v0 -> proto_files; + + } + + diff --git a/doc/source/packaging/diag/pyansys_namespace_diag.rst b/doc/source/packaging/diag/pyansys_namespace_diag.rst new file mode 100644 index 00000000..3c00946e --- /dev/null +++ b/doc/source/packaging/diag/pyansys_namespace_diag.rst @@ -0,0 +1,49 @@ +.. _pyansys_namespace_diag: +.. graphviz:: + :caption: Namespace convention for PyAnsys projects. + :alt: Namespace convention for PyAnsys projects. + :align: center + + digraph "sphinx-ext-graphviz" { + size="8,6"; + rankdir="LR"; + graph [ + fontname="Verdana", fontsize="10", color="black", fillcolor="white" + ]; + node [ + fontname="Verdana", fontsize="10", style="filled", color="black", fillcolor="#ffc107" + ]; + + pyproduct_library [ + label="pyproduct-library", shape="folder" + ]; + + src [ + label="src", shape="folder" + ] + + ansys [ + label="ansys", shape="folder" + ] + + product [ + label="product", shape="folder" + ] + + library [ + label="library", shape="folder" + ] + + init_file [ + label="__init__.py", shape="file" + ] + + pyproduct_library -> src; + src -> ansys; + ansys -> product; + product -> library; + library -> init_file; + + } + + diff --git a/doc/source/packaging/diag/pyproduct_library_structure_diag.rst b/doc/source/packaging/diag/pyproduct_library_structure_diag.rst new file mode 100644 index 00000000..11efeb04 --- /dev/null +++ b/doc/source/packaging/diag/pyproduct_library_structure_diag.rst @@ -0,0 +1,81 @@ +.. _pyproduct library structure diag: +.. graphviz:: + :caption: Minimum required PyAnsys project structure. + :alt: Minimum required PyAnsys project structure. + :align: center + + digraph "sphinx-ext-graphviz" { + size="8,6"; + rankdir="LR"; + graph [ + fontname="Verdana", fontsize="10", color="black", fillcolor="white", splines=ortho + ]; + node [ + fontname="Verdana", fontsize="10", style="filled", color="black", fillcolor="#ffc107" + ]; + + pyproduct_library [ + label="PyAnsys Project", shape="folder" + ]; + + doc [ + label="doc", shape="folder" + ] + + src [ + label="src", shape="folder" + ] + + ansys [ + label="ansys", shape="folder" + ] + + product [ + label="product", shape="folder" + ] + + library [ + label="library", shape="folder" + ] + + init_file [ + label="__init__.py", shape="file" + ] + + tests [ + label="tests", shape="folder" + ] + + readme [ + label="README.rst", shape="file" + ] + + license [ + label="LICENSE", shape="file" + ] + + pyproject [ + label="pyproject.toml", shape="file" + ] + + setup [ + label="setup.py", shape="file" + ] + + + pyproduct_library -> doc; + pyproduct_library -> src; + pyproduct_library -> tests; + pyproduct_library -> license; + pyproduct_library -> readme; + pyproduct_library -> pyproject; + pyproduct_library -> setup; + + src -> ansys; + ansys -> product; + product -> library; + library -> init_file; + + } + + diff --git a/doc/source/packaging/diag/python_library_diag.rst b/doc/source/packaging/diag/python_library_diag.rst new file mode 100644 index 00000000..c9b4aa32 --- /dev/null +++ b/doc/source/packaging/diag/python_library_diag.rst @@ -0,0 +1,66 @@ +.. _python pkg lib diag: +.. graphviz:: + :caption: A Python library is a collection of packages. + :alt: A Python library is a collection of packages. + :align: center + + digraph "sphinx-ext-graphviz" { + size="6,4"; + rankdir="LR"; + graph [ + fontname="Verdana", fontsize="10", color="black", fillcolor="white" + ]; + node [ + fontname="Verdana", fontsize="10", style="filled", color="black", fillcolor="#ffc107" + ]; + + python_library [ + label="Python Library", shape="folder" + ]; + + pkg_A [ + label="Package A", shape="folder" + ]; + + pkg_B [ + label="Package B", shape="folder" + ]; + + pkg_other [ + label="...", shape="folder" + ]; + + python_library -> pkg_A + python_library -> pkg_B + python_library -> pkg_other + + sub_package [ + label="Sub-package A", shape="folder" + ]; + + init_file1 [ + label="__init__.py", shape="file" + ]; + + init_file2 [ + label="__init__.py", shape="file" + ]; + + module_foo [ + label="module_foo.py", shape="file" + ]; + + module_bar [ + label="module_bar.py", shape="file" + ]; + + pkg_A -> sub_package; + pkg_A -> init_file1; + pkg_A -> module_foo; + + sub_package -> init_file2; + sub_package -> module_bar; + + } + + diff --git a/doc/source/packaging/diag/src_structure_diag.rst b/doc/source/packaging/diag/src_structure_diag.rst new file mode 100644 index 00000000..41a70b86 --- /dev/null +++ b/doc/source/packaging/diag/src_structure_diag.rst @@ -0,0 +1,64 @@ +.. _src structure diag: +.. graphviz:: + :caption: Generic structure for the src/ directory. + :alt: Generic structure for the src/ directory. + :align: center + + digraph "sphinx-ext-graphviz" { + size="8,6"; + rankdir="LR"; + graph [ + fontname="Verdana", fontsize="10", color="black", fillcolor="white" + ]; + node [ + fontname="Verdana", fontsize="10", style="filled", color="black", fillcolor="#ffc107" + ]; + + src [ + label="src", shape="folder" + ]; + + ansys [ + label="ansys", shape="folder" + ] + + product [ + label="product", shape="folder" + ] + + library [ + label="library", shape="folder" + ] + + pkg_A [ + label="package_A", shape="folder" + ] + + init_file_pkgA [ + label="__init__.py", shape="file" + ] + + other_files_pkgA [ + label="...", shape="file" + ] + + init_file [ + label="__init__.py", shape="file" + ] + + other_files [ + label="...", shape="file" + ] + + src -> ansys; + ansys -> product; + product -> library; + library -> init_file; + library -> other_files; + library -> pkg_A; + pkg_A -> init_file_pkgA; + pkg_A -> other_files_pkgA; + + } + + diff --git a/doc/source/packaging/diag/tests_structure_diag.rst b/doc/source/packaging/diag/tests_structure_diag.rst new file mode 100644 index 00000000..eccfab35 --- /dev/null +++ b/doc/source/packaging/diag/tests_structure_diag.rst @@ -0,0 +1,39 @@ +.. _tests structure diag: +.. graphviz:: + :caption: Minimum required PyAnsys project structure. + :alt: Minimum required PyAnsys project structure. + :align: center + + digraph "sphinx-ext-graphviz" { + size="8,6"; + rankdir="LR"; + graph [ + fontname="Verdana", fontsize="10", color="black", fillcolor="white" + ]; + node [ + fontname="Verdana", fontsize="10", style="filled", color="black", fillcolor="#ffc107" + ]; + + tests [ + label="tests", shape="folder" + ]; + + tests_package_A [ + label="tests_package_A", shape="folder" + ]; + + test_module_foo [ + label="test_module_foo.py", shape="file" + ] + + test_module_bar [ + label="test_module_bar.py", shape="file" + ] + + tests -> tests_package_A; + tests -> test_module_foo; + tests_package_A -> test_module_bar; + + } + + diff --git a/doc/source/packaging/doc_directory.rst b/doc/source/packaging/doc_directory.rst deleted file mode 100644 index 10ccad9c..00000000 --- a/doc/source/packaging/doc_directory.rst +++ /dev/null @@ -1,16 +0,0 @@ -Documentation Directory -####################### -The directory ``doc`` contains the documentation for the library -package, including: - -- The same information as in the README file in the root directory - of the library's repository. You should reuse the ``README.rst`` - file if possible to avoid duplication. -- In-depth getting started information, including installation details. -- API reference containing `Sphinx autosummary API documentation - `_. -- User guide containing basic examples, thorough descriptions of the library, - use case scenarios, and descriptive examples explaining methodologies. -- Examples consisting of `Jupyter Notebooks `_. -- Contributing section, which can be linked to the general "Contributing" - section in this developer's guide. diff --git a/doc/source/packaging/index.rst b/doc/source/packaging/index.rst index c0cd15c8..53b55a8c 100644 --- a/doc/source/packaging/index.rst +++ b/doc/source/packaging/index.rst @@ -19,9 +19,9 @@ general pattern for a PyAnsys library ensures: Ansys products * Unit testing, release packaging, and documentation -This figure shows the general pattern that each PyAnsys library should follow: +This diagram shows the general pattern that each PyAnsys library should follow: -.. image:: ../images/diagram.png +.. image:: diag/architecture_diag.png :alt: Overview Diagram The Ansys product or service exposes an interface that is locally @@ -40,17 +40,9 @@ model and API. :hidden: :maxdepth: 3 - template - library_structure - library_names - repo_directory_structure - ci_cd - source_organization - readme_file - setup_file - doc_directory - license - packaging.rst + templates + structure + build-systems .. _gRPC: https://grpc.io/ .. _pythoncom: http://timgolden.me.uk/pywin32-docs/pythoncom.html diff --git a/doc/source/packaging/library_names.rst b/doc/source/packaging/library_names.rst deleted file mode 100644 index d2b930f9..00000000 --- a/doc/source/packaging/library_names.rst +++ /dev/null @@ -1,53 +0,0 @@ -Project, Repository, and Library Names -###################################### -Large organizations providing Python packages -follow a consistent naming pattern. Ansys follows -these naming conventions: - -- The project name is to be ``Py``. For example, - ``PyMAPDL`` is the project name for MAPDL and ``PyAEDT`` - is the project name for AEDT. -- The repository name as hosted on GitHub should be all - lowercase to follow GitHub community standards. For - example, `pymapdl`_ and `pyaedt`_. -- The Python library name is to be in the format - ``ansys--``. For example, - `ansys-mapdl-core `_ - is the name for the core MAPDL library. - -Using long Python library names provides two primary advantages: - -- `Namespace Packages`_ can be used to designate official - Ansys packages -- Consistent branding and style can be applied to PyAnsys libraries - - -gRPC Interface Package ----------------------- -Lower level gRPC interface packages like `ansys-api-mapdl`_ should always be -named ``ansys-api-`` or may contain an additional level with -``ansys-api--``. - -This is to standarize the API packages: - -.. code:: - - ─ansys - │ ├───api - │ │ ├─── - │ │ │ ├───VERSION - │ │ │ ├───v1 - │ │ │ │ ├───sample.proto - - -Where the package name within ``sample.proto`` would be: - -.. code:: - - package ansys.api..v1; - - -.. _PyMAPDL: https://github.com/pyansys/pymapdl -.. _PyAEDT: https://github.com/pyansys/PyAEDT -.. _Namespace Packages: https://packaging.python.org/guides/packaging-namespace-packages/ -.. _ansys-api-mapdl: https://pypi.org/project/ansys-api-mapdl/ diff --git a/doc/source/packaging/library_structure.rst b/doc/source/packaging/library_structure.rst deleted file mode 100644 index 3a2b993a..00000000 --- a/doc/source/packaging/library_structure.rst +++ /dev/null @@ -1,12 +0,0 @@ -Library Structure -================= -All PyAnsys libraries are expected to follow a consistent pattern for: - - - Project, repository, and library names - - Repository directory structure - - Licensing - - Package configuration in the ``setup.py`` file - - Unit testing - - CI/CD using Azure DevOps or GitHub Workflow Actions - - Documentation - diff --git a/doc/source/packaging/license.rst b/doc/source/packaging/license.rst deleted file mode 100644 index 922a01c6..00000000 --- a/doc/source/packaging/license.rst +++ /dev/null @@ -1,10 +0,0 @@ -.. _license_file: - -License File -############ -PyAnsys libraries are expected to have this license file included in -the root directory of their repositories. - ------------- - -.. include:: ../../../LICENSE diff --git a/doc/source/packaging/packaging.rst b/doc/source/packaging/packaging.rst deleted file mode 100644 index 99a7008f..00000000 --- a/doc/source/packaging/packaging.rst +++ /dev/null @@ -1,323 +0,0 @@ -.. _packaging: - -Packaging -######### - -A Python package organizes and structures a Python library, which contains -several modules and assets such as examples or binary extensions. A Python -package offers an easy, reliable, and comprehensive way to distribute and -install a Python library on a variety of platforms and environments. - -.. note:: - - If you want to create a new PyAnsys project according to the guidelines - presented in the following lines, consider using the `ansys-templates tool`_. - - -Python Scripts, Modules, Sub-packages, and Packages ---------------------------------------------------- - -It is important to understand the difference between Python scripts, modules, -sub-packages, and packages: - -* ``Script``: Any Python file with logic source code. -* ``Module``: Any Python script hosted next to an ``__init__.py`` file. -* ``Sub-package``: Any directory containing various Python modules. -* ``Package``: Any directory containing Python modules and sub-packages. - -The following structure is shown to better explain previous concepts: - -.. code:: bash - - . - ├── src - │   └── package - │   ├── subpackage_a - │   │   ├── __init__.py - │   │   └── module_c.py - │   ├── __init__.py - │   ├── module_a.py - │   └── module_b.py - ├── LICENSE - ├── README.rst - └── pyproject.toml - - -Namespace Packaging -------------------- -A PyAnsys library uses `namespace packaging`_. Namespace packages allow you -to easily split sub-packages from a package into single, independent -distributions. - -There are different approaches available for creating a namespace package. For -the ``ansys`` namespace, we use the `PEP 420`_ `native namespace packages`_ -approach. - -Therefore, the source directory of any `PyAnsys library` should look like this: - -.. code:: bash - - . - └── src - └── ansys - └── product - └── library - └── __init__.py - - -Required Files --------------- - -* ``README.rst`` file: Describes the purpose of the package. - *The format of this file must be reStructuredText.* - -* ``LICENSE`` file: Specifies copyrights and required authorization. - -* ``pyproject.toml`` file: Provides package metadata and defines how the package - is built. There are different build backends available, such as `setuptools`_, - `poetry`_, and `flit`_. - -* ``src/ansys/product/library/__init__.py`` file: Usually contains the - version of the package in a variable named ``__version__``. The value of this - variable can be parsed from the ``pyproject.toml`` file so that the version - is only specified in one location. - - -Additional Directories ----------------------- - -The following directories may be specified at the same level as the ``src/`` one: - -* ``tests/``: Contains all unit tests for the package. It is - likely that these tests take advantage of the `pytest`_ framework. - -* ``doc/``: Contain all documentation files and examples on - how to use the package. - - -Project File and Build System ------------------------------- - -The ``pyproject.toml`` file is the standardized build configuration file for Python -projects. It must contain at least a ``[build-system]`` section, which determines -how the project is built. Some commonly used packaging tools are `setuptools`_, -`poetry`_, and `flit`_. All three of these packaging tools are currently supported by -the ``pyansys-advanced`` template, which is included in the `ansys-templates tool`_. - - -Flit -^^^^ - -Flit is a modern and lightweight build system that requires developers -to manage virtual environments on their own. Developers must: - -* Create a virtual environment and activate it. -* Install the package in editable mode. - -Flit is the default tool for creating a new ``pyansys`` project when using the -`ansys-templates tool`_. - -The ``[project]`` section specifies the project's metadata and required -dependencies. For more information, see `flit pyproject.toml -guidelines`_. - - -Poetry -^^^^^^ - -Because of its ``poetry.lock`` file, Poetry provides strong dependency pinning. When -installing a package, poetry creates a virtual environment, thus ensuring an isolated -package development environment. - -Nevertheless, it is possible to make Poetry ignore the `poetry.lock` file by running: - -.. code:: bash - - poetry config virtualenvs.create false --local - -Using `poetry`_ is popular because it: - -* Supports pinning dependency versions via a ``poetry.lock`` file that can be - used for testing and CI -* Allows downstream packages to still consume a loose dependency specification -* Integrates with `dependabot`_ to update the pinned version - -The ``[tool.poetry]`` section contains metadata and defines the project's -dependencies. For more information, see `poetry pyproject.toml documentation`_. - - -Setuptools -^^^^^^^^^^ - -Setuptools is a very well known build system in the Python ecosystem. It is used -in projects requiring a ``setup.py`` file and can be used in projects with a -``pyproject.toml`` file, although not all metadata in this second file -is fully supported yet. - -The main advantage of this build system is the ability to create custom build -steps in the form of Python code. - - -Specifying Package Version --------------------------- - -It is very common for packages to specify their current version in the -``__version__`` variable. This variable is usually declared in the -``__init__.py`` file included in the ``library`` directory. - -However, it is also required to specify the version in the ``pyproject.toml`` or -``setup.py`` file. This leads to a duplicate declaration of the project's version, -which could lead to a potential mismatch between both. - -Therefore, a good practice is to take advantage of the `importlib.metadata package`_ -for parsing the version from package metadata. This guarantees that there is no mismatch -between both version declarations. - - -.. code:: python - - try: - import importlib.metadata as importlib_metadata - except ModuleNotFoundError: - import importlib_metadata - - __version__ = importlib_metadata.version(__name__.replace(".", "-")) - - -Extra Tools Configuration -------------------------- - -There are plenty of tools in the Python ecosystem that enable developers to -write clean code according to different coding style guidelines. Some of these -tools are `black`_, `isort`_, `flake8`_, and `mypy`_. - -Some of these tools can be configured. This configuration might be specified in -custom files required by the tool or in the ``pyproject.toml`` file, thus reducing the -number of files in the project directory. - -.. note:: - - When using `setuptools`_ as a build backend, providing the metadata in - the ``pyproject.toml`` file is not yet fully supported. Instead, it also - requires a ``setup.cfg`` file, ``setup.py`` file, or both files. - -In the `pyansys template`, all these configurations are included by default in -the ``.pre-commit-config.yaml`` file because ``pre-commit`` is not able to parse the -``pyproject.toml`` file nor the ``setup.py`` file. - - -Generate the Package and Upload It on PyPI ------------------------------------------- - -The first time that you want to upload a package on PyPI under `ansys `_ -account, you must perform the following process manually. - -Create the python package. - -.. code:: - - pip install build - python -m build - -If using flit or poetry, you can also run: - -.. code:: - - flit build - poetry build - -Verify the distribution's long description rendering with ``twine``. - -.. code:: - - pip install twine - twine check dist/* - - -Upload the package to PyPI using ``twine`` and the upload token generated for -the ``ansys`` PyPI account. As soon as the package has been released for the -first time, it is possible to create an independent token dedicated to this -package. This way the token stored in the GitHub secrets and used in the -release's workflow is only related to that specific package. This limits the -exposure to any potential token security flaws. Contact -alexander.kaszynski@ansys.com for the token. - -.. code:: - - python -m twine upload -u __token__ -p --skip-existing dist/* - -For the next release upload, you can do it through the CI/CD workflow after generating a token just for this package. -Create a `secret`_ in GitHub settings. -Name it ``PYPI_TOKEN`` and assign it the token provided by PyPI. -This token will be reused in the CI/CD workflow handling the package distribution. - -Tag a Release -------------- -To deploy a new package on PyPI, you must tag a release under a release branch. The PyAnsys project -follows the `trunk-based development`_ source-control branching model, where the main development -branch is always in a releasable state. - -To tag the release, update your main local branch. - -.. code:: - - git checkout main - git pull - -Then, create a release branch. - -.. code:: - - git checkout -b release/MAJOR.MINOR - -Bump the version number in the ``_version`` file to ``MAJOR.MINOR.PATCH``. - -Commit and push your changes and then create the tag. - -.. code:: - - git commit -am "Increase version to v" - git tag v - git push --tags - -Following this tag creation, the workflow responsible for the distribution -will be automatically triggered. - -Install a Package ------------------ -Install a package with: - -.. code:: - - pip install ansys-- - -To create a package complying with the above standards, here is the minimal content of your PyAnsys library: - -.. code:: - - ansys///__init__.py - LICENSE - README.rst - pyproject.toml - tests/ - - -.. _namespace packaging: https://packaging.python.org/guides/packaging-namespace-packages/ -.. _native namespace packages: https://packaging.python.org/guides/packaging-namespace-packages/#native-namespace-packages -.. _PEP 420: https://www.python.org/dev/peps/pep-0420/ -.. _setuptools: https://setuptools.pypa.io -.. _poetry: https://python-poetry.org/docs/ -.. _flit pyproject.toml guidelines: https://flit.readthedocs.io/en/latest/pyproject_toml.html -.. _flit: https://flit.readthedocs.io -.. _dependabot: https://docs.github.com/en/code-security/supply-chain-security/keeping-your-dependencies-updated-automatically/about-dependabot-version-updates -.. _ansys-templates tool: https://github.com/pyansys/pyansys-templates -.. _poetry pyproject.toml documentation: https://python-poetry.org/docs/pyproject/ -.. _black: https://black.readthedocs.io/en/stable/usage_and_configuration/the_basics.html#configuration-via-a-file -.. _mypy: https://mypy.readthedocs.io/en/stable/config_file.html#the-mypy-configuration-file -.. _trunk-based development: https://trunkbaseddevelopment.com/ -.. _secret: https://docs.github.com/en/actions/reference/encrypted-secrets -.. _setup.py: https://packaging.python.org/tutorials/packaging-projects/#configuring-metadata -.. _importlib.metadata package: https://docs.python.org/3/library/importlib.metadata.html -.. _isort: https://github.com/PyCQA/isort -.. _flake8: https://flake8.pycqa.org/en/latest/ -.. _pytest: https://docs.pytest.org/en/latest/ diff --git a/doc/source/packaging/readme_file.rst b/doc/source/packaging/readme_file.rst deleted file mode 100644 index e07ac735..00000000 --- a/doc/source/packaging/readme_file.rst +++ /dev/null @@ -1,26 +0,0 @@ -README File -########### -Each PyAnsys library should have a README file in the root directory. -To create this file, use either `reStructuredText Markup Syntax`_ to -create a file named ``README.rst`` or `Markdown Syntax`_ to create a file -named ``README.md`` - -While Markdown syntax has better GitHub support, text in RST files can -be reused within Sphinx documentation, which avoids duplicating any -auto-generated Sphinx pages. For example, see `pyansys-sphinx-theme index.rst`_. - -.. _pyansys-sphinx-theme index.rst: https://github.com/pyansys/pyansys-sphinx-theme/blob/main/doc/source/index.rst -.. _reStructuredText Markup Syntax: https://docutils.sourceforge.io/rst.html -.. _Markdown Syntax: https://www.markdownguide.org/basic-syntax/ - - -The README file should at the minimum contain these elements: - -- PyAnsys library title -- General description -- Installation directions (via ``pip install`` and ``git clone``) -- Basic usage -- Links to the full documentation - -The README file is also reused within the ``long_description`` in -the package's ``setup.py`` file. diff --git a/doc/source/packaging/repo_directory_structure.rst b/doc/source/packaging/repo_directory_structure.rst deleted file mode 100644 index 21979929..00000000 --- a/doc/source/packaging/repo_directory_structure.rst +++ /dev/null @@ -1,33 +0,0 @@ -.. _repo_dir_struct: - -Repository Directory Structure ------------------------------- -The source for a PyAnsys library is hosted in an individual -repository under the `PyAnsys Organization Account -`__. The repository should contain -the source, documentation, and unit testing of the library in -this directory structure: - -:: - - .ci/azure-pipelines.yml (optional) - .github/workflows/ci.yml - ansys/ - / - / - __init__.py - my_module.py - my_other_module.py - doc/ - conf.py - index.rst - requirements.txt - LICENSE - README.rst - requirements.txt - setup.py - tests/ - requirements.txt - test_basic.py - test_advanced.py - diff --git a/doc/source/packaging/setup_file.rst b/doc/source/packaging/setup_file.rst deleted file mode 100644 index bcc6717b..00000000 --- a/doc/source/packaging/setup_file.rst +++ /dev/null @@ -1,56 +0,0 @@ -.. _setup_file: - -Setup File -########## -The setup file for a PyAnsys library package, ``setup.py``, should contain -these elements: - -- Name (such as ``ansys-mapdl-core``) -- Packages (such as ``ansys.mapdl.core``) -- Short description -- Long description in a ``README.md`` or ``README.rst`` file -- `Single-sourced package version `_ -- Author of ``"ANSYS, Inc."`` -- Maintainer's name and email -- Dependency requirements -- Applicable classifiers - -The ``ansys--`` would have at the minimum -the following within its ``setup.py``. - -.. code:: python - - """Setup file for ansys--""" - import codecs - import os - from io import open as io_open - from setuptools import setup - - THIS_PATH = os.path.abspath(os.path.dirname(__file__)) - __version__ = None - version_file = os.path.join(THIS_PATH, 'ansys', '', - '', '_version.py') - with io_open(version_file, mode='r') as fd: - exec(fd.read()) - - setup( - name='ansys--', - packages=['ansys..'], - version=__version__, - description='Short description', - long_description=open('README.rst').read(), - long_description_content_type='text/x-rst', - url='https://github.com/pyansys/pyansys-package-example/', - license='MIT', - author='ANSYS, Inc.', - maintainer='First Last', - maintainer_email='first.last@ansys.com', - install_requires=['grpcio>=1.30.0'], - python_requires='>=3.5', - classifiers=[ - 'Development Status :: 4 - Beta', - 'Programming Language :: Python :: 3', - 'License :: OSI Approved :: MIT License', - 'Operating System :: OS Independent', - ], - ) diff --git a/doc/source/packaging/source_organization.rst b/doc/source/packaging/source_organization.rst deleted file mode 100644 index cc5b7e54..00000000 --- a/doc/source/packaging/source_organization.rst +++ /dev/null @@ -1,47 +0,0 @@ -Source Organization -################### -PyAnsys libraries follow the `Namespace Packages`_ convention to allow -multiple libraries to use the same shared ``ansys`` namespace: - -``ansys///`` - -For example, the `PyMAPDL`_ library with the ``ansys-mapdl-core`` package -name has the following directory structure: - -:: - - setup.py - ansys/ - mapdl/ - core/ - __init__.py - launcher.py - mapdl_grpc.py - ... - -This allows the `PyMAPDL`_ library to be imported with: - -.. code:: python - - >>> from ansys.mapdl import core as pymapdl - -When using this convention, other namespace packages can use the -``ansys-mapdl`` namespace. - -.. code:: python - - >>> from ansys.mapdl import reader as pymapdl_reader - -.. note:: - - Do not include ``__init__.py`` in first-level and second-level - directories. If ``__init__.py`` is included in these levels, - namespace packages will conflict, allowing only one to be imported. - -While the ``ansys-`` namespace is verbose, using it -consistently is important because it allows multiple products and services -to share the same namespace. This makes it easy when searching for "ansys" -packages within the `Python Package Index PyPi `_. - -.. _Namespace Packages: https://packaging.python.org/guides/packaging-namespace-packages/ -.. _PyMAPDL: https://github.com/pyansys/pymapdl diff --git a/doc/source/packaging/structure.rst b/doc/source/packaging/structure.rst new file mode 100644 index 00000000..d259df5d --- /dev/null +++ b/doc/source/packaging/structure.rst @@ -0,0 +1,301 @@ +.. _ref_project_structure: + +################# +Project Structure +################# + +Most of the projects in the PyAnsys ecosystem ship in the form of a Python +library with other additional files. All these files form what it is called a +"project". A project can be uploaded to a repository to better track the changes +applied to it. + + +Naming Convention +================= + +Large organizations providing Python packages follow a consistent naming +pattern. Ansys follows two naming conventions, depending on the nature of the project. + +PyAnsys Library +--------------- + +- The project name is to be ``Py``. For example, ``PyMAPDL`` is the + project name for MAPDL and ``PyAEDT`` is the project name for AEDT. + +- The repository name as hosted on GitHub should be all lowercase to follow + GitHub community standards. For example, `pymapdl`_ and `pyaedt`_. + +- The Python library name is to be in the format + ``ansys--``. For example, `ansys-mapdl-core + `_ is the name for the core MAPDL + library. + +.. include:: diag/pyansys_namespace_diag.rst + +The previous structure leads to the following namespace when executing the import +statement: + +.. code:: python + + import ansys.product import library + +Using long Python library names provides two primary advantages: + +- `Namespace Packages`_ can be used to designate official Ansys packages. +- Consistent branding and style can be applied to PyAnsys libraries. + + +gRPC Interface Package +---------------------- +Lower-level gRPC interface packages like `ansys-api-mapdl`_ should always be +named ``ansys-api-`` and may contain an additional level: +``ansys-api--``. + +.. include:: diag/grpc_structure_diag.rst + +This structure leads to the following namespace within ``*.proto`` files: + +.. code:: + + package ansys.api..v0; + + +Python Libraries +================ + +A Python library is the formal way of distributing Python source code. It allows +for reuse and for specifying Python code dependencies. The guidance presented in this section +is compliant with the `Python Packaging Authority`_ and PyAnsys recommendations. + +.. note:: + + The best way to keep up to date with Python packaging is to check the `Python + Packaging User Guide`_, maintained by the `Python Packaging Authority`_ (PyPA). + PyAnsys guidelines are built on top of PyPA guidelines. + + +Scripts, Modules, Sub-packages, and Packages +-------------------------------------------- + +To understand the structure of a Python Library, it is important to know +the difference between Python scripts, modules, sub-packages, and packages. + +* ``Script``: Any Python file with logic source code +* ``Module``: Any Python script hosted next to an ``__init__.py`` file +* ``Sub-package``: Any directory containing various Python modules +* ``Package``: Any directory containing Python modules and sub-packages + + +Differences Between a Python Package and Library +------------------------------------------------ + +Although the terms *package* and *library* are often used interchangeably, there is +a key difference between them. A Python package is a collection of Python modules and +sub-packages, while a Python Library is a collection of Python packages. Figure +:numref:`python pkg lib diag` exposes this. + +.. include:: diag/python_library_diag.rst + + +Required Files for a PyAnsys Project +==================================== + +The structure of any PyAnsys library contains these files and directories: + +.. include:: diag/pyproduct_library_structure_diag.rst + +Descriptions follow for some of the directories in the structure: + +- ``doc/`` contains files related to documentation, guidelines, and examples. + +- ``src/`` contains all Python modules and scripts that form the project. + +- ``tests/`` contains all unit tests for checking the integrity of the project. + +- ``setup.py`` or ``pyproject.toml`` is the project file. + + +The ``doc/`` Directory +---------------------- + +When distributing software, it is important to document it. Documenting software +means giving guidelines on how to install it and describing all functions, +methods, and classes that it ships with. Case scenarios and examples should also +be part of the documentation. + +A PyAnsys project should have the following documentation sections: + +- ``Getting Started``: Defines requirements and provides installation information +- ``User Guide``: Explains how to use the software +- ``API Reference``: Describes the source code +- ``Examples``: Provides use case scenarios that demonstrate the capabilities of the software +- ``Contributing``: Supplies project-specific contribution guides and can link to general PyAnsys contribution guidelines + +Projects in the PyAnsys ecosystem take advantage of `Sphinx`_, a tool used for +building documentation for Python-based projects. As shown in :numref:`doc structure diag`, +`Sphinx`_ requires a ``doc/`` directory with a specific structure: + + +.. include:: diag/doc_structure_diag.rst + +- ``_build`` contains the rendered documentation in various formats, such as HTML + and PDF. + +- ``source`` contains the RST files that will be rendered when building the + documentation. + +- ``make.bat`` and ``Makefile`` are used to automate cleaning and building + commands. You use ``make.bat`` when running on Windows and ``Makefile`` + when running on MacOS or Linux. + +The ``source/`` directory must contain at least these files: + +- ``conf.py`` is a Python script used to declare the configuration of `Sphinx`_. +- ``index.rst`` is the index page of the documentation. In this file, try to reuse the + ``README.rst`` file to avoid duplication. + +If you would like to include images or documents, add them in the ``_static/`` +directory. + + +The ``src/`` Directory +---------------------- + +All the Python source code must be located in the ``src/`` directory. This is where the +build system will look when generating the wheel and source distributions. + +.. warning:: + + Folders inside the ``src/`` directory cannot contain spaces or hyphens. Replace these + characters with an underscore '_'. + +The structure of the ``src/`` directory determines the namespace of the PyAnsys +library. A namespace allow you to easily split sub-packages from a package into +single, independent distributions. + +There are different approaches available for creating a namespace package. For +the Ansys namespace, we use the `PEP 420`_ `native namespace packages`_ approach. + +Therefore, the source directory of any PyAnsys library must look like the one +shown in diagram :numref:`src structure diag`: + +.. include:: diag/src_structure_diag.rst + + +The ``tests/`` Directory +------------------------ + +To guarantee the integrity of a PyAnsys project, a good test suite is required. +PyAnsys projects use the `pytest`_ testing framework. + +A good practice is to emulate the structure of the ``src/ansys/product/library`` +directory, although this is not always necessary. + +.. include:: diag/tests_structure_diag.rst + +Notice the use of ``tests_*/`` when creating new directories inside the +``tests/`` one. For unit testing files, names use the ``test_*.py`` prefix. +This is the preferred way of naming directories and files inside the +``tests/`` directory. + + +The ``LICENSE`` File +-------------------- + +The ``LICENSE`` file provides the legal framework for the software. `PyAnsys`_ +projects must use `MIT License`_. A template for +this license is provided below: + +.. include:: code/license_mit_code.rst + +.. note:: + + Just because a software does not ship with a LICENSE file, it does not mean + it is free or open source. If you need to use unlicensed software, contact + its development team so they can provide you with the correct license. + + +The ``README.rst`` File +----------------------- + +Each PyAnsys library should have a ``README.rst`` file in the root directory. + +The preferred format of this file is `reStructuredText Markup Syntax`_, +although `Markdown Syntax`_ can be used too. While Markdown syntax has better +GitHub support, ReStructured Text (RST) files can be reused within Sphinx documentation. +This avoids duplication between the ``README.rst`` and the main ``index.rst`` in +the ``doc/source/`` directory. + +The ``README.rst`` file should at the minimum contain these elements: + +- PyAnsys library title +- General description +- Installation directions (via ``pip install`` and ``git clone``) +- Basic usage +- Links to the full documentation + +The ``README.rst`` file is also reused within the project file metadata. It is +usually included in the ``long-description`` field. + + +The ``pyproject.toml`` File +--------------------------- + +`PEP 518`_ introduced the usage of a project file named +``pyproject.toml``. + +The ``pyproject.toml`` file is mandatory because it allows ``pip`` to resolve the +requirements for building the library. The following tabs expose the ``[build-system]`` section +for some of the most popular build-system backend tools in the Python ecosystem: + +.. include:: code/pyproject_code.rst + + +The ``setup.py`` File +--------------------- + +For a long time, the ``setup.py`` file was generally used to build and +distribute Python libraries. Unlike a static ``pyproject.toml`` file, the +``setup.py`` file is a Python script. This means that Python code is interpreted +when building the library. This approach supports customizing the build +process but can also introduce security issues. + +.. note:: + + The ``setup.py`` is only compatible with `setuptools`_. Consider using a + ``pyproject.toml`` file instead. + + +While a ``setup.cfg`` file can be used to specify the metadata and packages, the ``setup.py`` +file must also be present. For more information, see: + +* `Building and Distributing Packages with Setuptools`_ +* `Configuring setuptools using setup.cfg files`_ + +As a minimum configuration for a PyAnsys project, the following ``setup.py`` +template can be used: + + +.. include:: code/setup_file_code.rst + + +.. REFERENCES & LINKS + +.. _MIT License: https://opensource.org/licenses/MIT +.. _PEP 420: https://peps.python.org/pep-0420/ +.. _native namespace packages: https://packaging.python.org/en/latest/guides/packaging-namespace-packages/#native-namespace-packages +.. _Namespace Packages: https://packaging.python.org/guides/packaging-namespace-packages/ +.. _PyAnsys: https://docs.pyansys.com/ +.. _Python Packaging User Guide: https://packaging.python.org/en/latest/ +.. _Python Packaging Authority: https://www.pypa.io/en/latest/ +.. _pytest: https://docs.pytest.org/en/latest/ +.. _Sphinx: https://www.sphinx-doc.org/en/master/ +.. _PyMAPDL: https://github.com/pyansys/pymapdl +.. _PyAEDT: https://github.com/pyansys/PyAEDT +.. _ansys-api-mapdl: https://pypi.org/project/ansys-api-mapdl/ +.. _reStructuredText Markup Syntax: https://docutils.sourceforge.io/rst.html +.. _Markdown Syntax: https://www.markdownguide.org/basic-syntax/ +.. _PEP 518: https://peps.python.org/pep-0518/ +.. _Building and Distributing Packages with Setuptools: https://setuptools.pypa.io/en/latest/setuptools.html +.. _Configuring setuptools using setup.cfg files: https://setuptools.pypa.io/en/latest/userguide/declarative_config.html +.. _setuptools: https://setuptools.pypa.io/en/latest/index.html diff --git a/doc/source/packaging/template.rst b/doc/source/packaging/templates.rst similarity index 79% rename from doc/source/packaging/template.rst rename to doc/source/packaging/templates.rst index 9f0e8c14..2949dd5e 100644 --- a/doc/source/packaging/template.rst +++ b/doc/source/packaging/templates.rst @@ -1,19 +1,20 @@ -################ -Library Template -################ - -Starting a new project from scratch is a tedious task. For this reason, the -`ansys-templates`_ tool was created to simplify the starting process and -make it more dynamic. Any rendered project will be compliant with the latest -PyAnsys coding and API style guidelines. - -The ansys-templates tool -======================== - -The `ansys-templates`_ is a command line interface which provides you with a -collection of templates. This tool allows you to create new PyAnsys projects -from scratch in a dynamic way by asking you several questions before generating -the new project. +.. _templates: + +######### +Templates +######### + +Starting a new project from scratch is a tedious task. To simplify the starting process +and make it more dynamic, the `ansys-templates`_ tool was created . Using this +template ensures that any project rendered will be compliant with the latest PyAnsys +coding and API style guidelines. + +The ``ansys-templates`` tool +============================ + +The `ansys-templates`_ tool is a command line interface that provides a +collection of templates. When you use this tool to create a new PyAnsys project, your +responses to the several questions that are asked result in dynamic project generation. Please, follow the `ansys-templates installation guide`_ to get the latest stable version installed in your system. diff --git a/requirements_docs.txt b/requirements_docs.txt index 2506bf21..e533b40c 100644 --- a/requirements_docs.txt +++ b/requirements_docs.txt @@ -2,3 +2,5 @@ Sphinx==4.5.0 pyansys-sphinx-theme==0.2.3 sphinx-copybutton==0.5.0 sphinx_toolbox==2.18.0 +sphinx-tabs>=1.2.1,<=3.2.0 +sphinxcontrib-mermaid>=0.7.1