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#
Do Not Repeat Yourself (DRY)
If it’s worth doing, it’s worth doing well.
Use abstraction and inheritance smartly.
Use
@property
getters, and if needed setters. Setters must verify and clean user inputs. For the most part usemake_prop_val_node()
, andmake_prop_pointer()
.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.
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.
Do it right the first time.
Document all functions.
Expect everything to mutate at any time.
Avoid relative imports when possible. Use top level ones instead: e.g.,
import montepy.cell.Cell
.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#
Spaces not tabs with 4 spaces for an indent.
External imports before internal imports with a blank line in between. All imports are alphabetized.
Naming Conventions#
Follow PEP 8 naming conventions e.g.,
lower_case_with_underscores
for variables, methods, functions, and module names, etc.CapitalizedWords
for class namesMCNP_ClassName
is an exception. For all Other acronyms use:AcronymMoreWords
. Above all, prioritize legibility.
UPER_CASE_WITH_UNDERSCORES
for pseudo-constant variables_single_leading_underscore
should be used for almost all internal attributes.__double_leading_underscore
should be used for private internal attributes that should not be accessed by users or sub-classes.
Variables should be nouns/noun-phrases
Functions/methods should be verb/verb-phrases.
Properties/attributes of classes should be nouns or
is_adjective
phrases.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:
.When appropriate names should mirror Python core libraries (e.g.,
NumberedObjectCollection
tries to mirror methods ofdict
,list
, andset
).Within reason: avoid abbreviating words. Above all, prioritize legibility.
For user facing functions and attributes, short names are best. (
surface_constants()
, really should have beenconstants
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#
One line descriptions.
Type annotations in the function signature
Description of all inputs.
Description of return values (can be skipped for None).
.. versionadded::
/.. versionchanged::
information for all new functions and classes. This information can be dropped with major releases.Example code for showing how to use objects that implement atypical
__dunders__
, e.g., for__setitem__
,__iter__
, etc.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.
Highly Recommended.#
A class level
.. seealso:
section referencing the user manuals.An examples code block. These should start with a section header: “Exampes”. All code blocks should use sphinx doctest.
Note
MontePy docstrings features custom commands for linking to MCNP user manuals.
These in general follow the :manual62:
, :manual63:
, :manual631:
pattern.
The MCNP 6.2.0 manual only supports linking to a specific page, and not a section, so the argument it takes is a
page number: :manual62:`123`
: becomes MCNP 6.2 manual p. 123.
The MCNP 6.3 manuals do support linking to section anchors.
By default the command links to a \\subsubsection
, e.g., :manual63:`5.6.1`
becomes: MCNP 6.3.0 manual § 5.6.1.
For other sections see: doc/source/conf.py
.
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:
Do not write any new tests using
unittest.TestCase
.Use
assert
and notself.assert...
, even if it’s available.parametrizing is preferred over verbose tests.
Use fixtures.
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:
Unit tests are in their own files for each class or a group of classes.
Integration tests go in
tests/test_*integration.py
. New integration files are welcome.Interface tests with other libraries, e.g.,
pickle
go intests/test_interface.py
.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 ofself
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:
Try not to break too much.
Warn with a
DeprecationWarning
if the deprecated function is still usable. Otherwiseraise
it as anException
.Add clear documentation on the fact it is deprecated and what the alternative is.
Write a migration plan, preferably it should be part of the releases prior the major release.
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:
Do not break anything
Warn with a
DeprecationWarning
(orPendingDeprecationWarning
, orFutureWarning
as appropriate. See the guide on warnings.)Add clear documentation on the fact it is deprecated and what the alternative is.
Clear these warnings and documentation notations at the next major release.