Skip to content

Commit

Permalink
Merge branch 'main' into fallout_summary_range
Browse files Browse the repository at this point in the history
  • Loading branch information
cmbenne3 authored Jan 14, 2025
2 parents eb283a2 + d4cc805 commit 11fb92a
Show file tree
Hide file tree
Showing 5 changed files with 114 additions and 40 deletions.
24 changes: 0 additions & 24 deletions aviary/docs/developer_guide/doctape.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -82,41 +82,17 @@
" doctape.glue_variable(key, md_code=True)\n",
" class_list += f'- `{key}` {val}\\n'\n",
"\n",
"# testing_list = ''\n",
"# for key,val in testing_functions.items():\n",
"# testing_list += f'- `{key}` {val}\\n'\n",
"\n",
"utility_list = '```{eval-rst}\\n'\n",
"for key in utility_functions:\n",
" doctape.glue_variable(key, md_code=True)\n",
" utility_list += ' '*4+f'.. autofunction:: aviary.utils.doctape.{key}\\n{\" \"*8}:noindex:\\n\\n'\n",
"utility_list += '```'\n",
"\n",
"# testing_list = '```{eval-rst}\\n'\n",
"# for key in testing_functions:\n",
"# utils.glue_variable(key, md_code=True)\n",
"# testing_list += ' '*4+f'.. autofunction:: aviary.utils.doctape.{key}\\n{\" \"*8}:noindex:\\n\\n'\n",
"# testing_list += '```'\n",
"\n",
"# testing_list = '<details>\\n\\n<summary>Function Docs</summary>\\n\\n'\n",
"testing_list = '```{eval-rst}\\n'\n",
"for key in testing_functions:\n",
" doctape.glue_variable(key, md_code=True)\n",
" testing_list += ' '*4+f'.. autofunction:: aviary.utils.doctape.{key}\\n{\" \"*8}:noindex:\\n\\n'\n",
"testing_list += '```'\n",
"# testing_list += '\\n\\n</details>'\n",
"\n",
"# glue_list = ''\n",
"# for key,val in glue_functions.items():\n",
"# glue_list += f'- `{key}` {key}\\n'\n",
"\n",
"# glue_list = ''\n",
"# for key in glue_functions:\n",
"# # doc_str = inspect.getdoc(imported_functions[key])\n",
"# doc_str = imported_functions[key].__doc__.split('\\n')[1]\n",
"# # doc_str = '\\n'.join([s+' ' for s in imported_functions[key].__doc__.split('\\n')])\n",
"# print(doc_str)\n",
"# glue_list += f'- `{key}`: {doc_str}\\n'\n",
"\n",
"glue_list = '```{eval-rst}\\n'\n",
"for key in glue_functions:\n",
Expand Down
73 changes: 69 additions & 4 deletions aviary/docs/developer_guide/doctape_examples.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -407,12 +407,12 @@
"outputs": [],
"source": [
"# Testing Cell\n",
"from aviary.api import Mission\n",
"from aviary.api import Aircraft\n",
"from aviary.utils.doctape import glue_variable, get_previous_line, get_variable_name\n",
"\n",
"glue_variable('value', Mission.Design.MACH, md_code=True)\n",
"glue_variable('value', Aircraft.Design.EMPTY_MASS, md_code=True)\n",
"glue_variable('var_value_code', get_previous_line(), md_code=True)\n",
"glue_variable(get_variable_name(Mission.Design.MACH), md_code=True)\n",
"glue_variable(get_variable_name(Aircraft.Design.EMPTY_MASS), md_code=True)\n",
"glue_variable('var_name_code', get_previous_line(), md_code=True)\n"
]
},
Expand All @@ -423,7 +423,7 @@
"If you want to glue the name of a variable, instead of the value that variable holds, you can use the {glue:md}`get_variable_name` to extract it.\n",
"\n",
"For example:\n",
"Using {glue:md}`var_value_code` will result in {glue:md}`value`, whereas using {glue:md}`var_name_code` will result in {glue:md}`Mission.Design.MACH`\n",
"Using {glue:md}`var_value_code` will result in {glue:md}`value`, whereas using {glue:md}`var_name_code` will result in {glue:md}`Aircraft.Design.EMPTY_MASS`\n",
"\n",
"### {glue:md}`get_attribute_name`\n",
"allows users to get the name of object attributes in order to glue them into documentation. This works well for Enums or Class Variables that have unique values."
Expand Down Expand Up @@ -482,6 +482,71 @@
"p1_alt = get_value(simplified_dict, 'phase1.altitude.val')\n",
"print(p1_alt)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"tags": [
"remove-cell"
]
},
"outputs": [],
"source": [
"# Testing Cell\n",
"from aviary.utils.doctape import glue_variable, check_args, get_all_keys, get_previous_line\n",
"from aviary.api import Aircraft, Mission\n",
"\n",
"glue_variable(Aircraft.__name__)\n",
"glue_variable(Mission.__name__)\n",
"\n",
"track_layers = 'track_layers'\n",
"check_args(get_all_keys, track_layers)\n",
"glue_variable(track_layers)\n",
"\n",
"get_all_keys(Mission, track_layers='Mission')\n",
"track_layers_with_name = get_previous_line().split(', ')[1].split(')')[0]\n",
"glue_variable('track_layers_with_Mission', track_layers_with_name, display=False)\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"These can also be used to recursively get all of the attributes from a complex object, like the {glue:md}`Aircraft` or {glue:md}`Mission` hierarchies.\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"from aviary.utils.doctape import get_all_keys, get_value, glue_keys\n",
"from aviary.api import Mission\n",
"\n",
"k1=get_all_keys(Mission)\n",
"print(k1[:5]) # Display the first 5 keys in Mission\n",
"k2=get_all_keys(Mission, track_layers=True)\n",
"print(k2[:5]) # Display the first 5 keys in Mission\n",
"k3=get_all_keys(Mission, track_layers='Mission')\n",
"print(k3[:5]) # Display the first 5 keys in Mission\n",
"\n",
"glue_keys(Mission, False)\n",
"\n",
"print(get_value(Mission,'Constraints.GEARBOX_SHAFT_POWER_RESIDUAL'))\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"If {glue:md}`get_all_keys` is used on an object like {glue:md}`Mission` without specifying a value for {glue:md}`track_layers` will return all of the uniquely named attributes of the object (such as {glue:md}GEARBOX_SHAFT_POWER_RESIDUAL). Setting {glue:md}`track_layers` to `True` will get all of the attributes in dot notation, but will not include the name of the original object ({glue:md}Constraints.GEARBOX_SHAFT_POWER_RESIDUAL). If you want the full name of the attribute, including the name of the original object, you can use that name as the value of {glue:md}`track_layers` (using {glue:md}track_layers_with_Mission gives us access to {glue:md}Mission.Constraints.GEARBOX_SHAFT_POWER_RESIDUAL)\n",
"\n",
"Using {glue:md}`glue_keys` handles this for us automatically by using the `__name__` attribute of the object passed to it as the value of {glue:md}`track_layers`.\n",
"\n",
"As with the dict_of_dicts, we can recusively get the value of an attribute using the full path along with {glue:md}`get_value`."
]
}
],
"metadata": {
Expand Down
43 changes: 37 additions & 6 deletions aviary/utils/doctape.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import inspect
import subprocess
import tempfile
import os
import numpy as np
import re


"""
Expand Down Expand Up @@ -89,6 +89,7 @@ def get_previous_line(n=1) -> str:
def get_variable_name(*variables) -> str:
"""
returns the name of the variable passed to the function as a string
# NOTE: You cannot call this function multiple times on one line
Parameters
----------
Expand All @@ -107,8 +108,20 @@ def get_variable_name(*variables) -> str:
# get the line number that called this function
lineno = pframe.f_lineno - first_line if first_line else pframe.f_lineno - 1
# extract the argument and remove all whitespace
arg: str = ''.join(lines[lineno].split(
'get_variable_name(')[1].split(')')[0].split())
arg = ''.join(lines[lineno].split()).split('get_variable_name(', 1)[1]

# Use regex to match balanced parentheses
match = re.match(r'([^()]*\([^()]*\))*[^()]*', arg)
if match:
arg = match.group(0)

# # Requires Python 3.11, but allows this to be called multiple times on one line
# positions = inspect.getframeinfo(pframe).positions
# calling_lines = lines[positions.lineno-1:positions.end_lineno]
# calling_lines[-1] = calling_lines[-1][:positions.end_col_offset-1]
# calling_lines[0] = calling_lines[0][positions.col_offset:].removeprefix('get_variable_name(')
# arg = ''.join([l.strip() for l in calling_lines])

if ',' in arg:
return arg.split(',')
else:
Expand Down Expand Up @@ -295,6 +308,7 @@ def get_attribute_name(object: object, attribute, error_type=AttributeError) ->
def get_all_keys(dict_of_dicts: dict, track_layers=False, all_keys=None) -> list:
"""
Recursively get all of the keys from a dict of dicts
This can also be used to recursively get all of the attributes from a complex object, like the Aircraft hierarchy
Note: this will not add duplicates of keys, but will
continue deeper even if a key is duplicated
Expand All @@ -314,9 +328,14 @@ def get_all_keys(dict_of_dicts: dict, track_layers=False, all_keys=None) -> list
all_keys : list
A list of all the keys in the dict_of_dicts
"""
if not isinstance(dict_of_dicts, dict):
dict_of_dicts = dict_of_dicts.__dict__
if all_keys is None:
all_keys = []

for key, val in dict_of_dicts.items():
if key.startswith('__') and key.endswith('__'):
continue
if track_layers is True:
current_layer = ''
elif track_layers:
Expand All @@ -325,7 +344,7 @@ def get_all_keys(dict_of_dicts: dict, track_layers=False, all_keys=None) -> list
key = current_layer+'.'+key
if key not in all_keys:
all_keys.append(key)
if isinstance(val, dict):
if isinstance(val, dict) or hasattr(val, '__dict__'):
if track_layers:
current_layer = key
else:
Expand All @@ -352,6 +371,8 @@ def get_value(dict_of_dicts: dict, comlpete_key: str):
"""

for key in comlpete_key.split('.'):
if not isinstance(dict_of_dicts, dict):
dict_of_dicts = dict_of_dicts.__dict__
dict_of_dicts = dict_of_dicts[key]
return dict_of_dicts

Expand All @@ -377,13 +398,18 @@ def glue_variable(name: str, val=None, md_code=False, display=True):
# local import so myst isn't required unless glue is being used
from myst_nb import glue
from IPython.display import Markdown
from IPython.utils import io
if val is None:
val = name
if md_code:
val = Markdown('`'+val+'`')
else:
val = Markdown(val)
glue(name, val, display)

with io.capture_output() as captured:
glue(name, val, display)
# if display:
captured.show()


def glue_keys(dict_of_dicts: dict, display=True) -> list:
Expand All @@ -400,7 +426,12 @@ def glue_keys(dict_of_dicts: dict, display=True) -> list:
all_keys : list
A list of all the keys that were glued
"""
all_keys = get_all_keys(dict_of_dicts)
if not isinstance(dict_of_dicts, dict):
track_layers = dict_of_dicts.__name__
else:
track_layers = False
all_keys = get_all_keys(dict_of_dicts, track_layers)

for key in all_keys:
glue_variable(key, md_code=True, display=display)
return all_keys
12 changes: 7 additions & 5 deletions aviary/utils/test/test_doctape.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,9 @@

from openmdao.utils.assert_utils import assert_near_equal, assert_equal_numstrings, assert_equal_arrays

from aviary.utils.doctape import gramatical_list, check_value, check_contains, check_args, run_command_no_file_error, get_attribute_name, get_all_keys, get_value, get_previous_line, get_variable_name
from aviary.utils.doctape import (gramatical_list, check_value, check_contains, check_args,
run_command_no_file_error, get_attribute_name, get_all_keys, get_value, get_previous_line,
get_variable_name, glue_variable, glue_keys)


class DocTAPETests(unittest.TestCase):
Expand Down Expand Up @@ -55,12 +57,12 @@ def test_get_variable_name(self):
assert_equal_numstrings(name, 'var')

# requires IPython shell
# def test_glue_variable(self):
# glue_variable('plain_text')
def test_glue_variable(self):
glue_variable('plain_text', display=False)

# requires IPython shell
# def test_glue_keys(self):
# glue_keys({'d1':{'d2':2}})
def test_glue_keys(self):
glue_keys({'d1': {'d2': 2}}, display=False)


if __name__ == '__main__':
Expand Down
2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@

pkgname = "aviary"
extras_require = {
"test": ["testflo", "pre-commit", "sphinx_book_theme==1.1.0"],
"test": ["testflo", "pre-commit", "sphinx_book_theme==1.1.0", "myst-nb"],
"examples": ["openaerostruct", "ambiance", "itables"],
}

Expand Down

0 comments on commit 11fb92a

Please sign in to comment.