Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

📚⬆️ UPDATE: sphinx v4 (+ sphinx extensions) #5420

Merged
merged 2 commits into from
Mar 9, 2022
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion aiida/engine/processes/workchains/restart.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ def validate_handler_overrides(
handler_overrides: Optional[orm.Dict],
ctx: 'PortNamespace' # pylint: disable=unused-argument
) -> Optional[str]:
"""Validator for the `handler_overrides` input port of the `BaseRestartWorkChain.
"""Validator for the `handler_overrides` input port of the `BaseRestartWorkChain`.

The `handler_overrides` should be a dictionary where keys are strings that are the name of a process handler, i.e. a
instance method of the `process_class` that has been decorated with the `process_handler` decorator. The values
Expand Down
1 change: 0 additions & 1 deletion aiida/engine/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -235,7 +235,6 @@ def loop_scope(loop) -> Iterator[None]:
Make an event loop current for the scope of the context

:param loop: The event loop to make current for the duration of the scope
:type loop: asyncio event loop
"""
current = asyncio.get_event_loop()

Expand Down
4 changes: 4 additions & 0 deletions aiida/manage/manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,14 +44,18 @@ class Manager:
AiiDA can have the following global resources loaded:

1. A single configuration object that contains:

- Global options overrides
- The name of a default profile
- A mapping of profile names to their configuration and option overrides

2. A single profile object that contains:

- The name of the profile
- The UUID of the profile
- The configuration of the profile, for connecting to storage and processing resources
- The option overrides for the profile

3. A single storage backend object for the profile, to connect to data storage resources
5. A single daemon client object for the profile, to connect to the AiiDA daemon
4. A single communicator object for the profile, to connect to the process control resources
Expand Down
4 changes: 2 additions & 2 deletions aiida/manage/tests/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ def manager(self) -> 'ProfileManager':
def use_temporary_profile(self, backend=None, pgtest=None):
"""Set up Test manager to use temporary AiiDA profile.

Uses :py:class:`aiida.manage.tests.TemporaryProfileManager` internally.
Uses :py:class:`aiida.manage.tests.main.TemporaryProfileManager` internally.

:param backend: Backend to use.
:param pgtest: a dictionary of arguments to be passed to PGTest() for starting the postgresql cluster,
Expand All @@ -108,7 +108,7 @@ def use_temporary_profile(self, backend=None, pgtest=None):
def use_profile(self, profile_name):
"""Set up Test manager to use existing profile.

Uses :py:class:`aiida.manage.tests.ProfileManager` internally.
Uses :py:class:`aiida.manage.tests.main.ProfileManager` internally.

:param profile_name: Name of existing test profile to use.
"""
Expand Down
2 changes: 1 addition & 1 deletion aiida/orm/querybuilder.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
:func:`QueryBuilder` is the frontend class that the user can use. It inherits from *object* and contains
backend-specific functionality. Backend specific functionality is provided by the implementation classes.

These inherit from :func:`aiida.orm.implementation.BackendQueryBuilder`,
These inherit from :func:`aiida.orm.implementation.querybuilder.BackendQueryBuilder`,
an interface classes which enforces the implementation of its defined methods.
An instance of one of the implementation classes becomes a member of the :func:`QueryBuilder` instance
when instantiated by the user.
Expand Down
5 changes: 1 addition & 4 deletions aiida/restapi/resources.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
from aiida.common.lang import classproperty
from aiida.restapi.common.exceptions import RestInputValidationError
from aiida.restapi.common.utils import Utils, close_thread_connection
from aiida.restapi.translator.nodes.node import NodeTranslator


class ServerInfo(Resource):
Expand Down Expand Up @@ -214,8 +215,6 @@ class QueryBuilder(BaseResource):
It supports POST requests taking in JSON :py:func:`~aiida.orm.querybuilder.QueryBuilder.as_dict`
objects and returning the :py:class:`~aiida.orm.querybuilder.QueryBuilder` result accordingly.
"""
from aiida.restapi.translator.nodes.node import NodeTranslator

_translator_class = NodeTranslator

GET_MESSAGE = (
Expand Down Expand Up @@ -339,8 +338,6 @@ class Node(BaseResource):
Differs from BaseResource in trans.set_query() mostly because it takes
query_type as an input and the presence of additional result types like "tree"
"""
from aiida.restapi.translator.nodes.node import NodeTranslator

_translator_class = NodeTranslator
_parse_pk_uuid = 'uuid' # Parse a uuid pattern in the URL path (not a pk)

Expand Down
2 changes: 1 addition & 1 deletion docs/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ default:

# Same as 'default' but does not exit at the first warning
debug:
$(SPHINXBUILD) -b html -n $(ALLSPHINXOPTS) $(BUILDDIR)/html
$(SPHINXBUILD) -b html -nW --keep-going $(ALLSPHINXOPTS) $(BUILDDIR)/html

all: html

Expand Down
158 changes: 133 additions & 25 deletions docs/source/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -62,8 +62,8 @@
extensions = [
'sphinx.ext.intersphinx', 'sphinx.ext.autodoc', 'sphinx.ext.doctest', 'sphinx.ext.viewcode', 'sphinx.ext.coverage',
'sphinx.ext.mathjax', 'sphinx.ext.ifconfig', 'sphinx.ext.todo', 'IPython.sphinxext.ipython_console_highlighting',
'IPython.sphinxext.ipython_directive', 'aiida.sphinxext', 'sphinx_design', 'sphinx_copybutton', 'sphinxext.rediraffe',
'notfound.extension', 'sphinx_sqlalchemy'
'IPython.sphinxext.ipython_directive', 'aiida.sphinxext', 'sphinx_design', 'sphinx_copybutton',
'sphinxext.rediraffe', 'notfound.extension', 'sphinx_sqlalchemy'
]

intersphinx_mapping = {
Expand All @@ -85,9 +85,10 @@
# see: https://pydata-sphinx-theme.readthedocs.io/en/latest/user_guide/configuring.html
html_theme = 'pydata_sphinx_theme'
html_theme_options = {
'external_links': [
{'url': 'http://www.aiida.net/', 'name': 'AiiDA Home'}
],
'external_links': [{
'url': 'http://www.aiida.net/',
'name': 'AiiDA Home'
}],
'github_url': 'https://github.com/aiidateam/aiida-core',
'twitter_url': 'https://twitter.com/aiidateam',
'use_edit_page_button': True,
Expand Down Expand Up @@ -125,10 +126,12 @@
copybutton_prompt_text = r'>>> |\.\.\. |(?:\(.*\) )?\$ |In \[\d*\]: | {2,5}\.\.\.: | {5,8}: '
copybutton_prompt_is_regexp = True

linkcheck_ignore = [r'http://localhost:\d+/',
r'http://127.0.0.1:\d+/',
r'http://www.wannier.org/support/',
r'https://github.com/aiidateam/aiida-diff/blob/92c61bdcc2db201d69da4d8b83a2b3f5dd529bf1/aiida_diff/data/__init__.py#L14-L20']
linkcheck_ignore = [
r'http://localhost:\d+/',
r'http://127.0.0.1:\d+/',
r'http://www.wannier.org/support/',
r'https://github.com/aiidateam/aiida-diff/blob/92c61bdcc2db201d69da4d8b83a2b3f5dd529bf1/aiida_diff/data/__init__.py#L14-L20',
]

# -- API documentation ---------------------------------------------------

Expand All @@ -144,7 +147,9 @@ def run_apidoc(_):
source_dir = os.path.abspath(os.path.dirname(__file__))
apidoc_dir = os.path.join(source_dir, 'reference', 'apidoc')
package_dir = os.path.join(source_dir, os.pardir, os.pardir, 'aiida')
exclude_api_patterns = []
exclude_api_patterns = [
os.path.join(package_dir, 'storage', 'psql_dos', 'migrations', 'versions'),
]

# In #1139, they suggest the route below, but for me this ended up
# calling sphinx-build, not sphinx-apidoc
Expand All @@ -160,51 +165,154 @@ def run_apidoc(_):
options = [
package_dir,
*exclude_api_patterns,
'-o', apidoc_dir,
'-o',
apidoc_dir,
'--private',
'--force',
'--no-headings',
'--module-first',
'--no-toc',
'--maxdepth', '4',
'--maxdepth',
'4',
]

# See https://stackoverflow.com/a/30144019
env = os.environ.copy()
env['SPHINX_APIDOC_OPTIONS'] = 'members,special-members,private-members,undoc-members,show-inheritance'
env['SPHINX_APIDOC_OPTIONS'
] = 'members,special-members,private-members,undoc-members,show-inheritance,ignore-module-all'
subprocess.check_call([cmd_path] + options, env=env)

def setup(app):
if os.environ.get('RUN_APIDOC', None) != 'False':
app.connect('builder-inited', run_apidoc)

# Warnings to ignore when using the -n (nitpicky) option
with open('nitpick-exceptions', 'r') as handle:
nitpick_ignore = [
tuple(line.strip().split(None, 1)) for line in handle.readlines() if line.strip() and not line.startswith('#')
]
# ignore anything ending in `Type`, since it is likely a `typing.TypeVar`, which sphinx-apidoc doesn't handle
nitpick_ignore_regex = [('py:.*', '.+Type')]
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm, this also includes all our custom ParameterType implementations in aiida.cmdline.params.types. Don't think it is the worst, but is there a way we can exclude those?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This just stops a warning from being emitted if it can't resolve the reference, it won't stop the reference from being resolved if present

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah I get that, but if there is warning related to our param types, that is probably because there is a real problem, and this way it will go unnoticed.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Eurgh, unless you are literally going to be using :py:obj:`aiida.cmdline.params.types.WrongType` in the documentation, or a docstring, I don't think it's going to be a big problem. Obviously, actual typing errors should be tested by mypy

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

But that is the whole point of these warnings, to warn you if you (by accident) type a wrong resource, for example a typo.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There are literally no sphinx references to ParamType anywhere in the documentation/docstrings, but anyhow I've removed this and reinstated a lovely long nitpick_exceptions list.

chrisjsewell marked this conversation as resolved.
Show resolved Hide resolved

# -- Non-html output formats options ---------------------------------------------------

htmlhelp_basename = 'aiidadoc'

latex_documents = [
('index', 'aiida.tex', 'AiiDA documentation',
author.replace(',',r'\and'), 'manual'),
('index', 'aiida.tex', 'AiiDA documentation', author.replace(',', r'\and'), 'manual'),
]

man_pages = [
('index', 'aiida', 'AiiDA documentation',
[author], 1)
]
man_pages = [('index', 'aiida', 'AiiDA documentation', [author], 1)]

texinfo_documents = [
('index', 'aiida', 'AiiDA documentation',
author, 'aiida', 'Automated Interactive Infrastructure and Database for Computational Science',
'Miscellaneous'),
(
'index', 'aiida', 'AiiDA documentation', author, 'aiida',
'Automated Interactive Infrastructure and Database for Computational Science', 'Miscellaneous'
),
]

epub_title = 'AiiDA'
epub_author = author
epub_publisher = author
epub_copyright = copyright

# -- Local extension --------------------------------------------------
from sphinx.addnodes import pending_xref
from sphinx.application import Sphinx
from sphinx.transforms import SphinxTransform


def setup(app: Sphinx):
if os.environ.get('RUN_APIDOC', None) != 'False':
app.connect('builder-inited', run_apidoc)
app.add_transform(AutodocAliases)


# these are mainly required, because sphinx finds multiple references,
# in `aiida.orm`` and `aiida.restapi.translator`
autodoc_aliases_typing = {
'Code': 'aiida.orm.nodes.data.code.Code',
'Computer': 'aiida.orm.computers.Computer',
'Data': 'aiida.orm.nodes.data.data.Data',
'Group': 'aiida.orm.groups.Group',
'Node': 'aiida.orm.nodes.node.Node',
'User': 'aiida.orm.users.User',
'ExitCode': 'aiida.engine.processes.exit_code.ExitCode',
'QueryBuilder': 'aiida.orm.querybuilder.QueryBuilder',
'WorkChainNode': 'aiida.orm.nodes.process.workflow.workchain.WorkChainNode',
'orm.ProcessNode': 'aiida.orm.nodes.process.process.ProcessNode',
'BackendAuthInfo': 'aiida.orm.implementation.authinfos.BackendAuthInfo',
'BackendComment': 'aiida.orm.implementation.comments.BackendComment',
'BackendComputer': 'aiida.orm.implementation.computers.BackendComputer',
'BackendGroup': 'aiida.orm.implementation.groups.BackendGroup',
'BackendLog': 'aiida.orm.implementation.logs.BackendLog',
'BackendNode': 'aiida.orm.implementation.nodes.BackendNode',
'BackendUser': 'aiida.orm.implementation.users.BackendUser',
}
# alias public APIs, exposed by __all__
autodoc_aliases_public = {
'aiida.common.CalcInfo': 'aiida.common.datastructures.CalcInfo',
'aiida.common.CodeInfo': 'aiida.common.datastructures.CodeInfo',
'aiida.engine.ProcessHandlerReport': 'aiida.engine.processes.workchains.utils.ProcessHandlerReport',
'aiida.engine.run': 'aiida.engine.launch.run',
'aiida.engine.submit': 'aiida.engine.launch.submit',
'aiida.engine.process_handler': 'aiida.engine.processes.workchains.utils.process_handler',
'aiida.engine.BaseRestartWorkChain': 'aiida.engine.processes.workchains.restart.BaseRestartWorkChain',
'aiida.engine.CalcJob': 'aiida.engine.processes.calcjobs.calcjob.CalcJob',
'aiida.engine.Process': 'aiida.engine.processes.process.Process',
'aiida.engine.WorkChain': 'aiida.engine.processes.workchains.workchain.WorkChain',
'aiida.engine.WorkChainSpec': 'aiida.engine.processes.workchains.workchain.WorkChainSpec',
'aiida.orm.ArrayData': 'aiida.orm.nodes.data.array.array.ArrayData',
'aiida.orm.Computer': 'aiida.orm.computers.Computer',
'aiida.orm.Group': 'aiida.orm.groups.Group',
'aiida.orm.Node': 'aiida.orm.nodes.node.Node',
'aiida.orm.User': 'aiida.orm.users.User',
'aiida.orm.CalculationNode': 'aiida.orm.nodes.process.calculation.calculation.CalculationNode',
'aiida.orm.CalcJobNode': 'aiida.orm.nodes.process.calculation.calcjob.CalcJobNode',
'aiida.orm.Code': 'aiida.orm.nodes.data.code.Code',
'aiida.orm.Data': 'aiida.orm.nodes.data.data.Data',
'aiida.orm.Dict': 'aiida.orm.nodes.data.dict.Dict',
'aiida.orm.ProcessNode': 'aiida.orm.nodes.process.process.ProcessNode',
'aiida.orm.RemoteData': 'aiida.orm.nodes.data.remote.base.RemoteData',
'aiida.orm.WorkChainNode': 'aiida.orm.nodes.process.workflow.workchain.WorkChainNode',
'aiida.orm.XyData': 'aiida.orm.nodes.data.array.xy.XyData',
'aiida.orm.load_computer': 'aiida.orm.utils.loaders.load_computer',
}
autodoc_aliases_exc = {}
for exc_name in (
'AiidaException', 'NotExistent', 'NotExistentAttributeError', 'NotExistentKeyError', 'MultipleObjectsError',
'RemoteOperationError', 'ContentNotExistent', 'FailedError', 'StoringNotAllowed', 'ModificationNotAllowed',
'IntegrityError', 'UniquenessError', 'EntryPointError', 'MissingEntryPointError', 'MultipleEntryPointError',
'LoadingEntryPointError', 'InvalidEntryPointTypeError', 'InvalidOperation', 'ParsingError', 'InternalError',
'PluginInternalError', 'ValidationError', 'ConfigurationError', 'ProfileConfigurationError',
'MissingConfigurationError', 'ConfigurationVersionError', 'IncompatibleStorageSchema', 'CorruptStorage',
'DbContentError', 'InputValidationError', 'FeatureNotAvailable', 'FeatureDisabled', 'LicensingException',
'TestsNotAllowedError', 'UnsupportedSpeciesError', 'TransportTaskException', 'OutputParsingError', 'HashingError',
'StorageMigrationError', 'LockedProfileError', 'LockingProfileError', 'ClosedStorage'
):
autodoc_aliases_exc[f'aiida.common.{exc_name}'] = f'aiida.common.exceptions.{exc_name}'


class AutodocAliases(SphinxTransform):
"""Replace autodoc aliases.

This converts references to objects in top-level modules to the actual object module,
for example, ``aiida.orm.Group`` to ``aiida.orm.groups.Group``.
"""

default_priority = 999

def apply(self, **kwargs) -> None:
for node in self.document.traverse(pending_xref):
if node.get('refdomain') != 'py':
continue
if node.get('reftype') == 'exc' and node.get('reftarget') in autodoc_aliases_exc:
node['reftarget'] = autodoc_aliases_exc[node.get('reftarget')]
elif node.get('reftarget').startswith('t.'):
node['reftarget'] = 'typing.' + node.get('reftarget')[2:]
elif node.get('reftarget') in autodoc_aliases_typing:
node['reftarget'] = autodoc_aliases_typing[node.get('reftarget')]
elif node.get('reftarget') in autodoc_aliases_public:
node['reftarget'] = autodoc_aliases_public[node.get('reftarget')]
elif node.get('reftarget').startswith('aiida.'):
for original, new in autodoc_aliases_public.items():
if node.get('reftarget').startswith(original + '.'):
node['reftarget'] = node.get('reftarget').replace(original, new)
break
14 changes: 7 additions & 7 deletions docs/source/developer_guide/core/internals.rst
Original file line number Diff line number Diff line change
Expand Up @@ -46,8 +46,8 @@ The :py:class:`~aiida.orm.nodes.node.Node` class has two important attributes:

The convention for all the :py:class:`~aiida.orm.nodes.node.Node` subclasses is that if a ``class B`` is inherited by a ``class A`` then there should be a package ``A`` under ``aiida/orm`` that has a file ``__init__.py`` and a ``B.py`` in that directory (or a ``B`` package with the corresponding ``__init__.py``)

An example of this is the :py:class:`~aiida.orm.nodes.data.array.ArrayData` and the :py:class:`~aiida.orm.nodes.data.array.kpoints.KpointsData`.
:py:class:`~aiida.orm.nodes.data.array.ArrayData` is placed in ``aiida/orm/data/array/__init__.py`` and :py:class:`~aiida.orm.nodes.data.array.kpoints.KpointsData` which inherits from :py:class:`~aiida.orm.nodes.data.array.ArrayData` is placed in ``aiida/orm/data/array/kpoints.py``
An example of this is the :py:class:`~aiida.orm.ArrayData` and the :py:class:`~aiida.orm.nodes.data.array.kpoints.KpointsData`.
:py:class:`~aiida.orm.ArrayData` is placed in ``aiida/orm/data/array/__init__.py`` and :py:class:`~aiida.orm.nodes.data.array.kpoints.KpointsData` which inherits from :py:class:`~aiida.orm.ArrayData` is placed in ``aiida/orm/data/array/kpoints.py``

This is an implicit & quick way to check the inheritance of the :py:class:`~aiida.orm.nodes.node.Node` subclasses.

Expand Down Expand Up @@ -260,22 +260,22 @@ Data

Navigating inputs and outputs
*****************************
- :py:meth:`~aiida.orm.nodes.data.Data.creator` returns either the :py:class:`~aiida.orm.nodes.process.calculation.CalculationNode` that created it or ``None`` if it was not created by a calculation.
- :py:meth:`~aiida.orm.Data.creator` returns either the :py:class:`~aiida.orm.CalculationNode` that created it or ``None`` if it was not created by a calculation.


ProcessNode
+++++++++++

Navigating inputs and outputs
*****************************
- :py:meth:`~aiida.orm.nodes.process.ProcessNode.caller` returns either the caller :py:class:`~aiida.orm.nodes.process.workflow.WorkflowNode` or ``None`` if it was not called by any process.
- :py:meth:`~aiida.orm.ProcessNode.caller` returns either the caller :py:class:`~aiida.orm.nodes.process.workflow.WorkflowNode` or ``None`` if it was not called by any process.

CalculationNode
+++++++++++++++

Navigating inputs and outputs
*****************************
- :py:meth:`~aiida.orm.nodes.process.calculation.CalculationNode.inputs` returns a :py:meth:`~aiida.orm.utils.managers.NodeLinksManager` object that can be used to access the node's incoming ``INPUT_CALC`` links.
- :py:meth:`~aiida.orm.CalculationNode.inputs` returns a :py:meth:`~aiida.orm.utils.managers.NodeLinksManager` object that can be used to access the node's incoming ``INPUT_CALC`` links.

The ``NodeLinksManager`` can be used to quickly go from a node to a neighboring node.
For example::
Expand Down Expand Up @@ -332,14 +332,14 @@ Navigating inputs and outputs

The ``.inputs`` manager for ``WorkflowNode`` and the ``.outputs`` manager both for ``CalculationNode`` and ``WorkflowNode`` work in the same way (see below).

- :py:meth:`~aiida.orm.nodes.process.calculation.CalculationNode.outputs` returns a :py:meth:`~aiida.orm.utils.managers.NodeLinksManager` object that can be used to access the node's outgoing ``CREATE`` links.
- :py:meth:`~aiida.orm.CalculationNode.outputs` returns a :py:meth:`~aiida.orm.utils.managers.NodeLinksManager` object that can be used to access the node's outgoing ``CREATE`` links.


.. _calculation updatable attributes:

Updatable attributes
********************
The :py:class:`~aiida.orm.nodes.process.ProcessNode` class is a subclass of the :py:class:`~aiida.orm.nodes.node.Node` class, which means that its attributes become immutable once stored.
The :py:class:`~aiida.orm.ProcessNode` class is a subclass of the :py:class:`~aiida.orm.nodes.node.Node` class, which means that its attributes become immutable once stored.
However, for a ``Calculation`` to be runnable it needs to be stored, but that would mean that its state, which is stored in an attribute can no longer be updated.
To solve this issue the :py:class:`~aiida.orm.utils.mixins.Sealable` mixin is introduced.
This mixin can be used for subclasses of ``Node`` that need to have updatable attributes even after the node has been stored in the database.
Expand Down
Loading