Development Standards#

Contributing#

Here is a getting started guide to contributing. If you have any questions Micah and Travis are available to give input and answer your questions. Before contributing you should review the MontePy Scope and design philosophy.

Versioning#

Version information is stored in git tags, and retrieved using setuptools scm. The version tag shall match the regular expression:

v\d\.\d+\.\d+(a\d+|\.post\d+)?.

These tags will be applied by a maintainer during the release process, and cannot be applied by normal users.

MontePy follows the semantic versioning standard and the PyPA specification for version specifiers to the best of our abilities.

The version numbers can be read as <Major>.<minor>.<patch>. Here is a quick summary of release types used, that is not meant to be authoritative:

  • Major release: This is a release that break backwards compatibility.

  • Minor release: This is a release the adds a new feature.

  • Patch release: This is a bug-fix release only.

  • Post release: This is a release that doesn’t change any code. This will add an extra \.post\d+ to the end of the previous version.

  • Alpha release: This is a testing release. Generally this is preparing for a major release. Features are not locked at this point, and may change. This is signified by adding a\d+ to the end of the next release.

Design Philosophy#

  1. Do Not Repeat Yourself (DRY)

  2. If it’s worth doing, it’s worth doing well.

  3. Use abstraction and inheritance smartly.

  4. Use @property getters, and if needed setters. Setters must verify and clean user inputs. For the most part use make_prop_val_node(), and make_prop_pointer().

  5. Fail early and politely. If there’s something that might be bad: the user should get a helpful error as soon as the error is apparent.

  6. Test. test. test. The goal is to achieve 100% test coverage. Unit test first, then do integration testing. A new feature merge request will ideally have around a dozen new test cases.

  7. Do it right the first time.

  8. Document all functions.

  9. Expect everything to mutate at any time.

  10. Avoid relative imports when possible. Use top level ones instead: e.g., import montepy.cell.Cell.

  11. Defer to vanilla python, and only use the standard library. Currently the only dependencies are numpy and sly. There must be good justification for breaking from this convention and complicating things for the user.

Style Guide#

  1. Thou shall be PEP 8, and use black.

  2. Spaces not tabs with 4 spaces for an indent.

  3. External imports before internal imports with a blank line in between. All imports are alphabetized.

Naming Conventions#

  1. Follow PEP 8 naming conventions e.g.,

    1. lower_case_with_underscores for variables, methods, functions, and module names, etc.

    2. CapitalizedWords for class names

      • MCNP_ClassName is an exception. For all Other acronyms use: AcronymMoreWords. Above all, prioritize legibility.

    3. UPER_CASE_WITH_UNDERSCORES for pseudo-constant variables

    4. _single_leading_underscore should be used for almost all internal attributes.

    5. __double_leading_underscore should be used for private internal attributes that should not be accessed by users or sub-classes.

  2. Variables should be nouns/noun-phrases

  3. Functions/methods should be verb/verb-phrases.

  4. Properties/attributes of classes should be nouns or is_adjective phrases.

  5. Collections should be a plural noun, and single instances should be singular. In loops there should be consistent names, e.g., for cell in cells:.

  6. When appropriate names should mirror Python core libraries (e.g., NumberedObjectCollection tries to mirror methods of dict, list, and set).

  7. Within reason: avoid abbreviating words. Above all, prioritize legibility.

  8. For user facing functions and attributes, short names are best. (surface_constants(), really should have been constants in hind-sight).

Doc Strings#

All public (not _private) classes and functions must have doc strings. Most _private classes and functions should still be documented for other developers. NumPy’s style guide is the standard used for MontePy doc strings.

Mandatory Elements#

  1. One line descriptions.

  2. Type annotations in the function signature

  3. Description of all inputs.

  4. Description of return values (can be skipped for None).

  5. .. versionadded::/ .. versionchanged:: information for all new functions and classes. This information can be dropped with major releases.

  6. Example code for showing how to use objects that implement atypical __dunders__, e.g., for __setitem__, __iter__, etc.

  7. Type hints on all new or modified functions.

Note

Class __init__ arguments are documented in the class docstrings and not in __init__.

Note

MontePy is in the process of migrating to type annotations, so not all functions will have them. Eventually MontePy may use a type enforcement engine that will use these hints. See #91 for more information. If you have issues with circular imports add the import: from __future__ import annotations, this is from PEP 563.

Example#

Here is the docstrings for Cell.

class Cell(Numbered_MCNP_Object):
    """Object to represent a single MCNP cell defined in CSG.

    Examples
    ^^^^^^^^

    First the cell needs to be initialized.

    .. testcode:: python

        import montepy
        cell = montepy.Cell()

    Then a number can be set.
    By default the cell is voided:

    .. doctest:: python

        >>> cell.number = 5
        >>> print(cell.material)
        None
        >>> mat = montepy.Material()
        >>> mat.number = 20
        >>> mat.add_nuclide("1001.80c", 1.0)
        >>> cell.material = mat
        >>> # mass and atom density are different
        >>> cell.mass_density = 0.1

    Cells can be inverted with ``~`` to make a geometry definition that is a compliment of
    that cell.

    .. testcode:: python

        complement = ~cell

    See Also
    --------

    * :manual631sec:`5.2`
    * :manual63sec:`5.2`
    * :manual62:`55`


    .. versionchanged:: 1.0.0

        Added number parameter

    Parameters
    ----------
    input : Union[Input, str]
        The Input syntax object this will wrap and parse.
    number : int
        The number to set for this object.
    """

    # snip

    def __init__(
        self,
        input: InitInput = None,
        number: int = None,
    ):

Testing#

Pytest is the official testing framework for MontePy. In the past it was unittest, and so the test suite is in a state of transition. Here are the principles for writing new tests:

  1. Do not write any new tests using unittest.TestCase.

  2. Use assert and not self.assert..., even if it’s available.

  3. parametrizing is preferred over verbose tests.

  4. Use fixtures.

  5. Use property based testing with hypothesis, when it makes sense. This is generally for complicated functions that users use frequently, such as constructors. See this tutorial for an introduction to property based testing.

Test Organization#

Tests are organized in the tests folder in the following way:

  1. Unit tests are in their own files for each class or a group of classes.

  2. Integration tests go in tests/test_*integration.py. New integration files are welcome.

  3. Interface tests with other libraries, e.g., pickle go in tests/test_interface.py.

  4. Test classes are preffered to organize tests by concepts. Each MontePy class should have its own test class. These should not subclass anything. Methods should accept _ instead of self to note that class structure is purely organizational.

Test Migration#

Currently the test suite does not conform to these standards fully. Help with making the migration to the new standards is appreciated. So don’t think something is sacred about a test file that does not follow these conventions.

Deprecation Guidelines#

Deprecation is an important part of the development life-cycle and a signal for users to help with migrations. Deprecations can occur either during a major release, or between major releases. The deprecation process is really part of a larger migration documentation process, and it provides a good last line of defense for users on how to migrate their code.

Note

See Versioning section for more details on release types.

Major Release Deprecations#

These are deprecations that occur during a major release. Generally these are deprecations necessary for the release to work, and must be at versions: Major.0.0. For these deprecations the guidelines are:

  1. Try not to break too much.

  2. Warn with a DeprecationWarning if the deprecated function is still usable. Otherwise raise it as an Exception.

  3. Add clear documentation on the fact it is deprecated and what the alternative is.

  4. Write a migration plan, preferably it should be part of the releases prior the major release.

  5. Only clear these DeprecationWarnings at the next major release.

Mid-Major Release Deprecations#

These are deprecations that are not during a major release. That is when the version matches: Major.Minor.0 or Major.Minor.Patch. The guidelines are:

  1. Do not break anything

  2. Warn with a DeprecationWarning (or PendingDeprecationWarning, or FutureWarning as appropriate. See the guide on warnings.)

  3. Add clear documentation on the fact it is deprecated and what the alternative is.

  4. Clear these warnings and documentation notations at the next major release.