Skip to content

Commit

Permalink
Merge pull request #8 from gtri/5-global-stage
Browse files Browse the repository at this point in the history
Top Level Context and Docs Update
  • Loading branch information
JamesArruda authored Dec 11, 2024
2 parents ecd7b53 + 3c1991c commit 0737538
Show file tree
Hide file tree
Showing 10 changed files with 112 additions and 30 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/documentation.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ jobs:
- name: Install dependencies
run: pip install .[docs]
- name: Build autodocs
run: sphinx-apidoc -o ./docs/source ./src/upstage_des ./src/upstage_des/test
run: sphinx-apidoc -o ./docs/source/auto ./src/upstage_des ./src/upstage_des/test
- name: Sphinx build
run: sphinx-build -b html docs/source _build
- name: Deploy to GitHub Pages
Expand Down
5 changes: 5 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,7 @@ celerybeat-schedule
.env
.report.json
.venv
.scripts

# VSCode
.vscode
Expand All @@ -123,3 +124,7 @@ tmp/
# Temporary Reporting Artifacts
classes.png
packages.png


# Built autodocs
docs/source/auto
4 changes: 2 additions & 2 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -86,8 +86,8 @@ Documentation is built from autodocs first, then the source build.
From the top level of the repo:

```bash
sphinx-apidoc -o ./docs/source ./src/upstage_des ./src/upstage_des/test
sphinx-build -b html ./docs/source ./build/docs
sphinx-apidoc -o ./docs/source/auto ./src/upstage_des ./src/upstage_des/test
sphinx-build -a -E -b html ./docs/source ./build/docs
```

Then the docs can be loaded from `./build/docs/index.html`
Expand Down
4 changes: 2 additions & 2 deletions docs/source/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ Unless you're adding to the codebase, you won't need to run the `sphinx-apidoc`

```console
(venv) $ pip install upstage-des[docs]
(venv) $ sphinx-apidoc -o .\docs\source\ .\src\upstage_des\ .\src\upstage_des\test\
(venv) $ sphinx-apidoc -o .\docs\source\auto .\src\upstage_des\ .\src\upstage_des\test\
(venv) $ sphinx-build -b html .\docs\source\ .\docs\build\
```

Expand Down Expand Up @@ -94,5 +94,5 @@ The API documentation is auto-generated.
:caption: API
:maxdepth: 2
modules.rst
auto/modules.rst
```
7 changes: 0 additions & 7 deletions docs/source/modules.rst

This file was deleted.

26 changes: 17 additions & 9 deletions docs/source/user_guide/how_tos/events.rst
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ All events accept a ``rehearsal_time_to_complete`` argument.
The available UPSTAGE events are:

:py:class:`~upstage_des.events.Event`
---------------------------------
-------------------------------------

Mimics SimPy's raw ``Event``, useful for marking pauses until a success.

Expand All @@ -32,7 +32,8 @@ One use case is the knowledge event, which enables a way to publish and event to
:py:class:`~upstage_des.events.Wait`
--------------------------------
------------------------------------

A standard SimPy timeout. Can be explicit or generate from a random uniform distribution.

The random uniform distribution accepts an input for the rehearsal time, while the base version rehearses at the given time.
Expand All @@ -45,7 +46,8 @@ The random uniform distribution accepts an input for the rehearsal time, while t
:py:class:`~upstage_des.events.Get`
-------------------------------
-----------------------------------

Get from a store or container.

.. code-block:: python
Expand All @@ -61,7 +63,8 @@ Get from a store or container.
:py:class:`~upstage_des.events.FilterGet`
-------------------------------------
-----------------------------------------

A get with a filter function, used for SimPy's ``FilterStore``.

.. code-block:: python
Expand All @@ -71,7 +74,8 @@ A get with a filter function, used for SimPy's ``FilterStore``.
:py:class:`~upstage_des.resources.sorted.SortedFilterGet`
-----------------------------------------------------
---------------------------------------------------------

A get with a filter or sorting function, used with :py:class:`~upstage_des.resources.sorted.SortedFilterStore`, and others.

.. code-block:: python
Expand All @@ -85,7 +89,8 @@ A get with a filter or sorting function, used with :py:class:`~upstage_des.resou
:py:class:`~upstage_des.events.Put`
-------------------------------
-----------------------------------

Put something into a store or container

.. code-block:: python
Expand All @@ -100,7 +105,8 @@ Put something into a store or container
:py:class:`~upstage_des.events.ResourceHold`
----------------------------------------
--------------------------------------------

Put and release holds on limited resources.

.. code-block:: python
Expand All @@ -115,7 +121,8 @@ Put and release holds on limited resources.
:py:class:`~upstage_des.events.All`
-------------------------------
-----------------------------------

Succeed when all passed events succeed.

.. code-block:: python
Expand All @@ -129,7 +136,8 @@ Succeed when all passed events succeed.
:py:class:`~upstage_des.events.Any`
-------------------------------
-----------------------------------

Succeed when any passed events succeed

.. code-block:: python
Expand Down
14 changes: 7 additions & 7 deletions docs/source/user_guide/how_tos/geography.rst
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ Geographic Data Types and State
These are the built-in features that use geography:

:py:class:`~upstage_des.data_types.GeodeticLocation`
------------------------------------------------
----------------------------------------------------

This data type stores a Latitude / Longitude / Altitude (optional) for a point around the globe.

Expand Down Expand Up @@ -108,7 +108,7 @@ Both distances are within .07% of UPSTAGE's calculations.


:py:class:`~upstage_des.states.GeodeticLocationChangingState`
---------------------------------------------------------
-------------------------------------------------------------

This is a State that allows activation and movement along great-circle waypoints with altitude changing along the waypoints. When initializing, it accepts a ``GeodeticLocation`` object, and it returns those when you ask it for
the state's value. Here is its basic usage:
Expand Down Expand Up @@ -156,7 +156,7 @@ Cartesian Locations
These aren't geographic, but they serve the same purpose, so we include them here.

:py:class:`~upstage_des.data_types.CartesianLocation`
-------------------------------------------------
-----------------------------------------------------

This data type stores an X / Y / Z (optional) location in 2 or 3D space (z is set to zero if not included).

Expand Down Expand Up @@ -190,7 +190,7 @@ The distance is always implied to be in ``distance_units``, without setting it.


:py:class:`~upstage_des.states.CartesianLocationChangingState`
----------------------------------------------------------
--------------------------------------------------------------

This active state works the exact same as the ``GeodeticLocationChangingState`` , except that it requires waypoints to be ``CartesianLocation`` s.

Expand All @@ -201,7 +201,7 @@ Geography Sub-Module
The :py:mod:`upstage_des.geography` module contains:

:py:class:`~upstage_des.geography.spherical.Spherical`
--------------------------------------------------
------------------------------------------------------

This class contains methods for finding distances, positions, and for segmenting great-circle paths on the assumption of a spherical earth.

Expand All @@ -214,7 +214,7 @@ The most useful methods, besides distance, may be:
#. :py:meth:`~upstage_des.geography.spherical.Spherical.point_from_bearing_dist`, which gives you a point relative to a base location at some distance and bearing.

:py:class:`~upstage_des.geography.wgs84.WGS84`
-------------------------------------------
----------------------------------------------

This class contains methods for finding distances, positions, and for segmenting great-circle paths on the assumption of a WGS84 ellipsoid. These methods take longer to run than the Spherical version,
so be sure the extra accuracy is worth it.
Expand All @@ -228,7 +228,7 @@ The most useful methods, besides distance, may be:
#. :py:meth:`~upstage_des.geography.spherical.WGS84.point_from_bearing_dist`, which gives you a point relative to a base location at some distance and bearing.

:py:mod:`upstage_des.geography.intersections`
-------------------------------------------
---------------------------------------------

The :py:func:`~upstage_des.geography.intersections.get_intersection_locations` function calculates an intersection between a great circle path and a sphere. It can be passed an instance of ``Spherical`` or ``WGS84``
to do distance calculations with.
Expand Down
29 changes: 27 additions & 2 deletions docs/source/user_guide/how_tos/stage_variables.rst
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ SimPy or UPSTAGE tasks, states, or processes.
Expected Stage Variables
===========================
=========================

Some variables are expected to exist on the stage for some features. These are found in the :py:class:`~upstage_des.base.StageProtocol` protocol,
and are listed below:
Expand Down Expand Up @@ -69,7 +69,7 @@ actors and tasks. The following snippets shows how you might use it for pure Sim
Accessing Stage through upstage_des.api
===================================
=======================================

For convenience, you can also do the following:

Expand All @@ -84,3 +84,28 @@ For convenience, you can also do the following:
assert stage.altitude_units == "centimeters"
altitude_units = UP.get_stage_variable("altitude_units")
assert altitude_units == "centimeters"
Accessing Stage outside of the EnvironmentContext
=================================================

There are some times when you may want the Stage to exist outside of the EnvironmentContext. When doing plotting of
geographic entities, for example, having access to the ``stage_model`` is useful. This is also helpful when visualizing
or doing analysis in Jupyter Notebooks, where you don't want to sit inside a context manager.

For this situation, UPSTAGE provides a way to operate the context manager without needing to be inside the context.

.. code-block:: python
import upstage_des.api as UP
from upstage_des.base import create_top_context, clear_top_context
ctx = create_top_context()
add_stage_variable("example", 1.234)
assert get_stage_variable("example") == 1.234
clear_top_context(ctx)
The two functions are just wrappers around the context manager's ``__enter__`` and ``__exit__`` methods, but they provide a clearer
idea of what's being done and why.
26 changes: 26 additions & 0 deletions src/upstage_des/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -526,3 +526,29 @@ def get_stage() -> StageProtocol:
except LookupError:
raise ValueError("Stage should have been set.")
return stage


def create_top_context(
initial_time: float = 0.0,
random_seed: int | None = None,
random_gen: Any | None = None,
) -> EnvironmentContext:
"""Create a stage at this level of context.
Makes your current level the same as the context manager.
Returns:
EnvironmentContext: The context
"""
ctx = EnvironmentContext(initial_time, random_seed, random_gen)
ctx.__enter__()
return ctx


def clear_top_context(ctx: EnvironmentContext) -> None:
"""Clear the context.
Args:
ctx (EnvironmentContext): The object made from create_stage()
"""
ctx.__exit__()
25 changes: 25 additions & 0 deletions src/upstage_des/test/test_stage.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,9 @@
UpstageBase,
UpstageError,
add_stage_variable,
get_stage_variable,
)
from upstage_des.base import clear_top_context, create_top_context


class Stager(UpstageBase): ...
Expand Down Expand Up @@ -38,3 +40,26 @@ def test_stage() -> None:
# After the context, it should not exists
with pytest.raises(UpstageError):
source.stage


def test_contextless_stage() -> None:
ctx = create_top_context()
add_stage_variable("example", 1.234)

assert get_stage_variable("example") == 1.234

# dropping into a new context ignores the above
with EnvironmentContext():
add_stage_variable("example", 8.675)
assert get_stage_variable("example") == 8.675

assert get_stage_variable("example") == 1.234

clear_top_context(ctx)

with pytest.raises(ValueError, match="Stage should have been set."):
get_stage_variable("example")


if __name__ == "__main__":
test_contextless_stage()

0 comments on commit 0737538

Please sign in to comment.