Skip to content

Commit ce94d59

Browse files
committed
Merge branch 'main' into feature/add-funnel-globalization-mechanism-to-trf
2 parents f496144 + 4e91199 commit ce94d59

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

49 files changed

+4044
-2309
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ __pycache__/
1616
*.egg-info/
1717

1818
# Documentation builds
19+
doc/OnlineDocs/api
1920
doc/OnlineDocs/_build
2021
doc/OnlineDocs/**/*.spy
2122

doc/OnlineDocs/contribution_guide.rst

Lines changed: 213 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,35 @@ at least 70% coverage of the lines modified in the PR and prefer coverage
7575
closer to 90%. We also require that all tests pass before a PR will be
7676
merged.
7777

78+
Tests must import the Pyomo test harness from
79+
``pyomo.common.unittest`` instead of using Python's built-in
80+
``unittest`` module directly. This wrapper extends the standard testing
81+
framework with Pyomo-specific capabilities, including support for test
82+
timeouts and Pyomo-specific assertions for comparing expressions and
83+
nested containers with numerical tolerance. Using the provided interface
84+
ensures that all tests run consistently across Pyomo's multiple CI environments.
85+
A small example is shown below:
86+
87+
.. code-block:: python
88+
89+
import pyomo.common.unittest as unittest
90+
91+
class TestSomething(unittest.TestCase):
92+
def test_basic(self):
93+
self.assertEqual(1 + 1, 2)
94+
95+
Developers can also use any of the predefined ``pytest`` markers to categorize
96+
their tests appropriately.
97+
Markers are declared in ``pyproject.toml``. Some commonly used markers are:
98+
99+
- ``expensive``: tests that take a long time to run
100+
- ``mpi``: tests that require MPI
101+
- ``solver(name)``: dynamic marker to label a test for a specific solver,
102+
e.g., ``@pytest.mark.solver("gurobi")``
103+
104+
More details about Pyomo-defined default test behavior can be found in
105+
the `conftest.py file <https://github.com/Pyomo/pyomo/blob/main/conftest.py>`_.
106+
78107
.. note::
79108
If you are having issues getting tests to pass on your Pull Request,
80109
please tag any of the core developers to ask for help.
@@ -336,33 +365,51 @@ Finally, move to the directory containing the clone of your Pyomo fork and run:
336365

337366
::
338367

339-
pip install -e .
368+
pip install -e .[tests,docs,optional]
340369

341-
These commands register the cloned code with the active python environment
342-
(``pyomodev``). This way, your changes to the source code for ``pyomo`` are
370+
This command registers the cloned code with the active Python environment
371+
(``pyomodev``) and installs all possible optional dependencies.
372+
Using ``-e`` ensures that your changes to the source code for ``pyomo`` are
343373
automatically used by the active environment. You can create another conda
344374
environment to switch to alternate versions of pyomo (e.g., stable).
345375

376+
.. note::
377+
378+
The ``optional`` and ``docs`` dependencies are not strictly required;
379+
however, we recommend installing them to ensure that a large number of
380+
tests can be run locally. Optional packages that are not available will
381+
cause tests to skip.
382+
346383
Review Process
347384
--------------
348385

349386
After a PR is opened it will be reviewed by at least two members of the
350387
core development team. The core development team consists of anyone with
351-
write-access to the Pyomo repository. Pull requests opened by a core
388+
write-access to the Pyomo repository. PRs opened by a core
352389
developer only require one review. The reviewers will decide if they
353390
think a PR should be merged or if more changes are necessary.
354391

355392
Reviewers look for:
356-
357-
* Outside of ``pyomo.contrib``: Code rigor and standards, edge cases,
358-
side effects, etc.
359-
* Inside of ``pyomo.contrib``: No “glaringly obvious” problems with
360-
the code
361-
* Documentation and tests
362393

363-
The core development team tries to review pull requests in a timely
364-
manner but we make no guarantees on review timeframes. In addition, PRs
365-
might not be reviewed in the order they are opened in.
394+
* **Core and Addons:** Code rigor, standards compliance, test coverage above
395+
a threshold, and avoidance of unintended side effects (e.g., regressions
396+
or backwards incompatibilities)
397+
* **Devel:** Basic code correctness and clarity, with an understanding that
398+
these areas are experimental and evolving
399+
* **All areas:** Code formatting (using ``black``), documentation, and tests
400+
401+
.. note::
402+
403+
For more information about Pyomo's development principles and the
404+
stability expectations for ``addons`` and ``devel``, see
405+
:doc:`/principles`.
406+
407+
The core development team tries to review PRs in a timely
408+
manner, but we make no guarantees on review timeframes.
409+
Smaller, focused PRs are preferred and are generally reviewed more quickly.
410+
Larger PRs require more review effort and may take significantly longer.
411+
In addition, PRs might not be reviewed in the order in which they are opened.
412+
366413

367414
Where to put contributed code
368415
-----------------------------
@@ -372,64 +419,171 @@ git repository. Next, you should create a branch on your fork dedicated
372419
to the development of the new feature or bug fix you're interested
373420
in. Once you have this branch checked out, you can start coding. Bug
374421
fixes and minor enhancements to existing Pyomo functionality should be
375-
made in the appropriate files in the Pyomo code base. New examples,
376-
features, and packages built on Pyomo should be placed in
377-
``pyomo.contrib``. Follow the link below to find out if
378-
``pyomo.contrib`` is right for your code.
379-
380-
``pyomo.contrib``
381-
-----------------
382-
383-
Pyomo uses the ``pyomo.contrib`` package to facilitate the inclusion
384-
of third-party contributions that enhance Pyomo's core functionality.
385-
The are two ways that ``pyomo.contrib`` can be used to integrate
386-
third-party packages:
387-
388-
* ``pyomo.contrib`` can provide wrappers for separate Python packages, thereby
389-
allowing these packages to be imported as subpackages of pyomo.
390-
391-
* ``pyomo.contrib`` can include contributed packages that are developed and
392-
maintained outside of the Pyomo developer team.
393-
394-
Including contrib packages in the Pyomo source tree provides a
422+
made in the appropriate files in the Pyomo code base.
423+
424+
We refer to the modules that form the foundation of the Pyomo environment
425+
as ``pyomo`` core. This includes the base expression systems, modeling
426+
components, model compilers, and solver interfaces. The core development
427+
team has committed to maintaining these capabilities, adhering to the
428+
strictest policies for testing and backwards compatibility.
429+
430+
Larger features, new modeling components, or experimental functionality
431+
should be placed in one of Pyomo's extension namespaces, described below.
432+
433+
Namespaces for Contributed and Experimental Code
434+
++++++++++++++++++++++++++++++++++++++++++++++++
435+
436+
Pyomo organizes non-core functionality into a small
437+
number of clearly defined namespaces. Contributors should place new
438+
functionality according to its intended stability and maintenance
439+
expectations:
440+
441+
* ``pyomo.addons`` – For mostly stable, supported extensions that build on
442+
the Pyomo core. These packages are maintained by dedicated
443+
contributors, follow Pyomo's coding and testing standards, and adhere
444+
to the same backwards compatibility and deprecation policies as the
445+
rest of the codebase.
446+
447+
* ``pyomo.devel`` – For experimental or rapidly evolving
448+
contributions. These modules serve as early experimentation for research ideas,
449+
prototypes, or specialized modeling components. Functionality under
450+
this namespace may change or be removed between releases without
451+
deprecation warnings.
452+
453+
* ``pyomo.unsupported`` - For contributions that no longer have an active
454+
maintainer nor any future development plans. Functionality under this namespace
455+
may not work and is **NOT** tested through the standard test harness.
456+
457+
This tiered namespace structure provides contributors a clear pathway from
458+
**experimentation to supported integration**, while protecting users from
459+
unexpected changes in stable areas of the codebase.
460+
461+
.. list-table::
462+
:header-rows: 1
463+
:widths: 20 30 50
464+
465+
* - Namespace
466+
- Intended Use
467+
- Stability
468+
* - ``pyomo.devel``
469+
- Active research and experimental code
470+
- Unstable; APIs may change without warning
471+
* - ``pyomo.addons``
472+
- Mostly stable, supported extensions maintained by contributors
473+
- Mostly stable APIs; follow Pyomo's standards
474+
* - ``pyomo.unsupported``
475+
- Unsupported, unmaintained code
476+
- No guarantee of functionality and no regular testing
477+
* - ``pyomo``
478+
- Core Pyomo modeling framework
479+
- Fully supported and versioned
480+
481+
Submitting a Contributed Package
482+
--------------------------------
483+
484+
Including contributed packages in the Pyomo source tree provides a
395485
convenient mechanism for defining new functionality that can be
396-
optionally deployed by users. We expect this mechanism to include
397-
Pyomo extensions and experimental modeling capabilities. However,
398-
contrib packages are treated as optional packages, which are not
399-
maintained by the Pyomo developer team. Thus, it is the responsibility
486+
optionally deployed by users. We expect this mechanism to include
487+
Pyomo extensions and experimental modeling capabilities. However,
488+
contributed packages are treated as optional packages, which are not necessarily
489+
maintained by the Pyomo developer team. Thus, it is the responsibility
400490
of the code contributor to keep these packages up-to-date.
401491

402-
Contrib package contributions will be considered as pull-requests,
403-
which will be reviewed by the Pyomo developer team. Specifically,
492+
Contributed packages will be considered as pull requests,
493+
which will be reviewed by the Pyomo developer team. Specifically,
404494
this review will consider the suitability of the proposed capability,
405495
whether tests are available to check the execution of the code, and
406496
whether documentation is available to describe the capability.
407-
Contrib packages will be tested along with Pyomo. If test failures
497+
Contributed packages will be tested along with Pyomo. If test failures
408498
arise, then these packages will be disabled and an issue will be
409-
created to resolve these test failures.
499+
created to resolve these test failures. The Pyomo team reserves the
500+
right to remove contributed packages that are not maintained.
501+
502+
When submitting a new package (under either ``addons`` or
503+
``devel``), please ensure that:
504+
505+
* The package has at least one maintainer responsible for its upkeep.
506+
* The code includes tests that can be run through Pyomo's
507+
continuous integration framework.
508+
* The package includes documentation that clearly describes its purpose and
509+
usage, preferably as online documentation in ``doc/OnlineDocs``.
510+
* Optional dependencies are properly declared in ``setup.py``
511+
under the appropriate ``[optional]`` section.
512+
* The contribution passes all standard style and formatting checks.
513+
514+
Example: Structure of a Contributed Package
515+
-------------------------------------------
516+
517+
This section illustrates a minimal example of how a contributed package
518+
may be structured within the ``pyomo.devel`` or ``pyomo.addons``
519+
namespaces. This example is provided for documentation purposes only
520+
and is not included as source code in the Pyomo repository.
521+
522+
Minimal Directory Layout
523+
++++++++++++++++++++++++
524+
525+
At a minimum, a contributed package should follow a structure similar
526+
to the following::
527+
528+
pyomo/devel/example_package/
529+
├── __init__.py
530+
├── core.py
531+
└── tests/
532+
├── __init__.py
533+
└── test_example_package.py
534+
535+
Package Initialization
536+
++++++++++++++++++++++
537+
538+
The package ``__init__.py`` file should expose the primary public
539+
interfaces of the package and avoid unnecessary imports. Contributed
540+
packages must be safe to import as optional components and should not
541+
introduce side effects at import time.
542+
543+
For example::
544+
545+
# pyomo/devel/example_package/__init__.py
546+
from pyomo.devel.example_package.core import example_function
547+
548+
Core Functionality
549+
++++++++++++++++++
550+
551+
The main functionality of the contributed package should be implemented
552+
in one or more modules within the package directory (for example,
553+
``core.py``). These modules should follow Pyomo's coding standards,
554+
documentation requirements, and dependency management policies.
410555

411-
Contrib Packages within Pyomo
412-
+++++++++++++++++++++++++++++
556+
Tests
557+
+++++
413558

414-
Third-party contributions can be included directly within the
415-
``pyomo.contrib`` package. The ``pyomo/contrib/example`` package
416-
provides an example of how this can be done, including a directory
417-
for plugins and package tests. For example, this package can be
418-
imported as a subpackage of ``pyomo.contrib``::
559+
All contributed packages must include tests. Tests should be placed in a
560+
``tests`` subpackage and use the Pyomo test harness provided by
561+
``pyomo.common.unittest``.
419562

420-
import pyomo.environ as pyo
421-
from pyomo.contrib.example import a
563+
At a minimum, tests should verify that the package can be imported and
564+
that its primary functionality executes as expected. For example::
422565

423-
# Print the value of 'a' defined by this package
424-
print(a)
566+
import pyomo.common.unittest as unittest
425567

426-
Although ``pyomo.contrib.example`` is included in the Pyomo source
427-
tree, it is treated as an optional package. Pyomo will attempt to
428-
import this package, but if an import failure occurs, Pyomo will
429-
silently ignore it. Otherwise, this pyomo package will be treated
430-
like any other. Specifically:
568+
class TestExamplePackage(unittest.TestCase):
569+
def test_import(self):
570+
import pyomo.devel.example_package
431571

432-
* Plugin classes defined in this package are loaded when ``pyomo.environ`` is loaded.
572+
Tests for contributed packages are run as part of the Pyomo
573+
test suite and must not have an unconditional import of optional dependencies.
574+
Tests that exercise functionality requiring optional dependencies must be
575+
properly guarded (e.g., with ``@unittest.skipIf()`` / ``@unittest.skipUnless()``).
576+
Pyomo provides a standard tool for supporting the delayed import of optional
577+
dependencies (see :py:`attempt_import()`) as well as a central location for
578+
importing many common optional dependencies (see :py:mod:`pyomo.common.dependencies`).
579+
For example, tests that require ``numpy`` may be marked using the Pyomo
580+
test harness as follows::
433581

434-
* Tests in this package are run with other Pyomo tests.
582+
import pyomo.common.unittest as unittest
583+
from pyomo.common.dependencies import numpy as np, numpy_available
435584

585+
@unittest.skipIf(not numpy_available, "NumPy is not available")
586+
class TestExampleWithNumpy(unittest.TestCase):
587+
def test_numpy_functionality(self):
588+
a = np.array([1, 2, 3])
589+
self.assertEqual(a.sum(), 6)

doc/OnlineDocs/getting_started/installation.rst

Lines changed: 7 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -42,18 +42,16 @@ the following in a shell:
4242
pip install pyomo
4343

4444

45-
Conditional Dependencies
46-
~~~~~~~~~~~~~~~~~~~~~~~~
45+
Optional Dependencies
46+
~~~~~~~~~~~~~~~~~~~~~
4747

4848
Extensions to Pyomo, and many of the contributions in ``pyomo.contrib``,
49-
often have conditional dependencies on a variety of third-party Python
49+
often depend on additional third-party Python
5050
packages including but not limited to: matplotlib, networkx, numpy,
51-
openpyxl, pandas, pint, pymysql, pyodbc, pyro4, scipy, sympy, and
52-
xlrd.
51+
openpyxl, pandas, pint, scipy, sympy, and xlrd.
5352

5453
A full list of optional dependencies can be found in Pyomo's
55-
``pyproject.toml`` under the ``[project.optional-dependencies]`` table.
56-
They can be displayed by running:
54+
``setup.py``. They can be displayed by running:
5755

5856
::
5957

@@ -65,14 +63,14 @@ They can be displayed by running:
6563
Pyomo extensions that require any of these packages will generate
6664
an error message for missing dependencies upon use.
6765

68-
When using *pip*, all conditional dependencies can be installed at once
66+
When using *pip*, all optional dependencies can be installed at once
6967
using the following command:
7068

7169
::
7270

7371
pip install 'pyomo[optional]'
7472

75-
When using *conda*, many of the conditional dependencies are included
73+
When using *conda*, many of the optional dependencies are included
7674
with the standard Anaconda installation.
7775

7876
You can check which Python packages you have installed using the command

0 commit comments

Comments
 (0)