Skip to content

Commit c03ba98

Browse files
authored
Merge branch 'OpenMDAO:main' into mount_location
2 parents a75a11a + d4cc805 commit c03ba98

File tree

5 files changed

+114
-40
lines changed

5 files changed

+114
-40
lines changed

aviary/docs/developer_guide/doctape.ipynb

Lines changed: 0 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -82,41 +82,17 @@
8282
" doctape.glue_variable(key, md_code=True)\n",
8383
" class_list += f'- `{key}` {val}\\n'\n",
8484
"\n",
85-
"# testing_list = ''\n",
86-
"# for key,val in testing_functions.items():\n",
87-
"# testing_list += f'- `{key}` {val}\\n'\n",
88-
"\n",
8985
"utility_list = '```{eval-rst}\\n'\n",
9086
"for key in utility_functions:\n",
9187
" doctape.glue_variable(key, md_code=True)\n",
9288
" utility_list += ' '*4+f'.. autofunction:: aviary.utils.doctape.{key}\\n{\" \"*8}:noindex:\\n\\n'\n",
9389
"utility_list += '```'\n",
9490
"\n",
95-
"# testing_list = '```{eval-rst}\\n'\n",
96-
"# for key in testing_functions:\n",
97-
"# utils.glue_variable(key, md_code=True)\n",
98-
"# testing_list += ' '*4+f'.. autofunction:: aviary.utils.doctape.{key}\\n{\" \"*8}:noindex:\\n\\n'\n",
99-
"# testing_list += '```'\n",
100-
"\n",
101-
"# testing_list = '<details>\\n\\n<summary>Function Docs</summary>\\n\\n'\n",
10291
"testing_list = '```{eval-rst}\\n'\n",
10392
"for key in testing_functions:\n",
10493
" doctape.glue_variable(key, md_code=True)\n",
10594
" testing_list += ' '*4+f'.. autofunction:: aviary.utils.doctape.{key}\\n{\" \"*8}:noindex:\\n\\n'\n",
10695
"testing_list += '```'\n",
107-
"# testing_list += '\\n\\n</details>'\n",
108-
"\n",
109-
"# glue_list = ''\n",
110-
"# for key,val in glue_functions.items():\n",
111-
"# glue_list += f'- `{key}` {key}\\n'\n",
112-
"\n",
113-
"# glue_list = ''\n",
114-
"# for key in glue_functions:\n",
115-
"# # doc_str = inspect.getdoc(imported_functions[key])\n",
116-
"# doc_str = imported_functions[key].__doc__.split('\\n')[1]\n",
117-
"# # doc_str = '\\n'.join([s+' ' for s in imported_functions[key].__doc__.split('\\n')])\n",
118-
"# print(doc_str)\n",
119-
"# glue_list += f'- `{key}`: {doc_str}\\n'\n",
12096
"\n",
12197
"glue_list = '```{eval-rst}\\n'\n",
12298
"for key in glue_functions:\n",

aviary/docs/developer_guide/doctape_examples.ipynb

Lines changed: 69 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -407,12 +407,12 @@
407407
"outputs": [],
408408
"source": [
409409
"# Testing Cell\n",
410-
"from aviary.api import Mission\n",
410+
"from aviary.api import Aircraft\n",
411411
"from aviary.utils.doctape import glue_variable, get_previous_line, get_variable_name\n",
412412
"\n",
413-
"glue_variable('value', Mission.Design.MACH, md_code=True)\n",
413+
"glue_variable('value', Aircraft.Design.EMPTY_MASS, md_code=True)\n",
414414
"glue_variable('var_value_code', get_previous_line(), md_code=True)\n",
415-
"glue_variable(get_variable_name(Mission.Design.MACH), md_code=True)\n",
415+
"glue_variable(get_variable_name(Aircraft.Design.EMPTY_MASS), md_code=True)\n",
416416
"glue_variable('var_name_code', get_previous_line(), md_code=True)\n"
417417
]
418418
},
@@ -423,7 +423,7 @@
423423
"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",
424424
"\n",
425425
"For example:\n",
426-
"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",
426+
"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",
427427
"\n",
428428
"### {glue:md}`get_attribute_name`\n",
429429
"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."
@@ -482,6 +482,71 @@
482482
"p1_alt = get_value(simplified_dict, 'phase1.altitude.val')\n",
483483
"print(p1_alt)"
484484
]
485+
},
486+
{
487+
"cell_type": "code",
488+
"execution_count": null,
489+
"metadata": {
490+
"tags": [
491+
"remove-cell"
492+
]
493+
},
494+
"outputs": [],
495+
"source": [
496+
"# Testing Cell\n",
497+
"from aviary.utils.doctape import glue_variable, check_args, get_all_keys, get_previous_line\n",
498+
"from aviary.api import Aircraft, Mission\n",
499+
"\n",
500+
"glue_variable(Aircraft.__name__)\n",
501+
"glue_variable(Mission.__name__)\n",
502+
"\n",
503+
"track_layers = 'track_layers'\n",
504+
"check_args(get_all_keys, track_layers)\n",
505+
"glue_variable(track_layers)\n",
506+
"\n",
507+
"get_all_keys(Mission, track_layers='Mission')\n",
508+
"track_layers_with_name = get_previous_line().split(', ')[1].split(')')[0]\n",
509+
"glue_variable('track_layers_with_Mission', track_layers_with_name, display=False)\n"
510+
]
511+
},
512+
{
513+
"cell_type": "markdown",
514+
"metadata": {},
515+
"source": [
516+
"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"
517+
]
518+
},
519+
{
520+
"cell_type": "code",
521+
"execution_count": null,
522+
"metadata": {},
523+
"outputs": [],
524+
"source": [
525+
"from aviary.utils.doctape import get_all_keys, get_value, glue_keys\n",
526+
"from aviary.api import Mission\n",
527+
"\n",
528+
"k1=get_all_keys(Mission)\n",
529+
"print(k1[:5]) # Display the first 5 keys in Mission\n",
530+
"k2=get_all_keys(Mission, track_layers=True)\n",
531+
"print(k2[:5]) # Display the first 5 keys in Mission\n",
532+
"k3=get_all_keys(Mission, track_layers='Mission')\n",
533+
"print(k3[:5]) # Display the first 5 keys in Mission\n",
534+
"\n",
535+
"glue_keys(Mission, False)\n",
536+
"\n",
537+
"print(get_value(Mission,'Constraints.GEARBOX_SHAFT_POWER_RESIDUAL'))\n"
538+
]
539+
},
540+
{
541+
"cell_type": "markdown",
542+
"metadata": {},
543+
"source": [
544+
"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",
545+
"\n",
546+
"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",
547+
"\n",
548+
"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`."
549+
]
485550
}
486551
],
487552
"metadata": {

aviary/utils/doctape.py

Lines changed: 37 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
import inspect
22
import subprocess
33
import tempfile
4-
import os
54
import numpy as np
5+
import re
66

77

88
"""
@@ -89,6 +89,7 @@ def get_previous_line(n=1) -> str:
8989
def get_variable_name(*variables) -> str:
9090
"""
9191
returns the name of the variable passed to the function as a string
92+
# NOTE: You cannot call this function multiple times on one line
9293
9394
Parameters
9495
----------
@@ -107,8 +108,20 @@ def get_variable_name(*variables) -> str:
107108
# get the line number that called this function
108109
lineno = pframe.f_lineno - first_line if first_line else pframe.f_lineno - 1
109110
# extract the argument and remove all whitespace
110-
arg: str = ''.join(lines[lineno].split(
111-
'get_variable_name(')[1].split(')')[0].split())
111+
arg = ''.join(lines[lineno].split()).split('get_variable_name(', 1)[1]
112+
113+
# Use regex to match balanced parentheses
114+
match = re.match(r'([^()]*\([^()]*\))*[^()]*', arg)
115+
if match:
116+
arg = match.group(0)
117+
118+
# # Requires Python 3.11, but allows this to be called multiple times on one line
119+
# positions = inspect.getframeinfo(pframe).positions
120+
# calling_lines = lines[positions.lineno-1:positions.end_lineno]
121+
# calling_lines[-1] = calling_lines[-1][:positions.end_col_offset-1]
122+
# calling_lines[0] = calling_lines[0][positions.col_offset:].removeprefix('get_variable_name(')
123+
# arg = ''.join([l.strip() for l in calling_lines])
124+
112125
if ',' in arg:
113126
return arg.split(',')
114127
else:
@@ -295,6 +308,7 @@ def get_attribute_name(object: object, attribute, error_type=AttributeError) ->
295308
def get_all_keys(dict_of_dicts: dict, track_layers=False, all_keys=None) -> list:
296309
"""
297310
Recursively get all of the keys from a dict of dicts
311+
This can also be used to recursively get all of the attributes from a complex object, like the Aircraft hierarchy
298312
Note: this will not add duplicates of keys, but will
299313
continue deeper even if a key is duplicated
300314
@@ -314,9 +328,14 @@ def get_all_keys(dict_of_dicts: dict, track_layers=False, all_keys=None) -> list
314328
all_keys : list
315329
A list of all the keys in the dict_of_dicts
316330
"""
331+
if not isinstance(dict_of_dicts, dict):
332+
dict_of_dicts = dict_of_dicts.__dict__
317333
if all_keys is None:
318334
all_keys = []
335+
319336
for key, val in dict_of_dicts.items():
337+
if key.startswith('__') and key.endswith('__'):
338+
continue
320339
if track_layers is True:
321340
current_layer = ''
322341
elif track_layers:
@@ -325,7 +344,7 @@ def get_all_keys(dict_of_dicts: dict, track_layers=False, all_keys=None) -> list
325344
key = current_layer+'.'+key
326345
if key not in all_keys:
327346
all_keys.append(key)
328-
if isinstance(val, dict):
347+
if isinstance(val, dict) or hasattr(val, '__dict__'):
329348
if track_layers:
330349
current_layer = key
331350
else:
@@ -352,6 +371,8 @@ def get_value(dict_of_dicts: dict, comlpete_key: str):
352371
"""
353372

354373
for key in comlpete_key.split('.'):
374+
if not isinstance(dict_of_dicts, dict):
375+
dict_of_dicts = dict_of_dicts.__dict__
355376
dict_of_dicts = dict_of_dicts[key]
356377
return dict_of_dicts
357378

@@ -377,13 +398,18 @@ def glue_variable(name: str, val=None, md_code=False, display=True):
377398
# local import so myst isn't required unless glue is being used
378399
from myst_nb import glue
379400
from IPython.display import Markdown
401+
from IPython.utils import io
380402
if val is None:
381403
val = name
382404
if md_code:
383405
val = Markdown('`'+val+'`')
384406
else:
385407
val = Markdown(val)
386-
glue(name, val, display)
408+
409+
with io.capture_output() as captured:
410+
glue(name, val, display)
411+
# if display:
412+
captured.show()
387413

388414

389415
def glue_keys(dict_of_dicts: dict, display=True) -> list:
@@ -400,7 +426,12 @@ def glue_keys(dict_of_dicts: dict, display=True) -> list:
400426
all_keys : list
401427
A list of all the keys that were glued
402428
"""
403-
all_keys = get_all_keys(dict_of_dicts)
429+
if not isinstance(dict_of_dicts, dict):
430+
track_layers = dict_of_dicts.__name__
431+
else:
432+
track_layers = False
433+
all_keys = get_all_keys(dict_of_dicts, track_layers)
434+
404435
for key in all_keys:
405436
glue_variable(key, md_code=True, display=display)
406437
return all_keys

aviary/utils/test/test_doctape.py

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,9 @@
33

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

6-
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
6+
from aviary.utils.doctape import (gramatical_list, check_value, check_contains, check_args,
7+
run_command_no_file_error, get_attribute_name, get_all_keys, get_value, get_previous_line,
8+
get_variable_name, glue_variable, glue_keys)
79

810

911
class DocTAPETests(unittest.TestCase):
@@ -55,12 +57,12 @@ def test_get_variable_name(self):
5557
assert_equal_numstrings(name, 'var')
5658

5759
# requires IPython shell
58-
# def test_glue_variable(self):
59-
# glue_variable('plain_text')
60+
def test_glue_variable(self):
61+
glue_variable('plain_text', display=False)
6062

6163
# requires IPython shell
62-
# def test_glue_keys(self):
63-
# glue_keys({'d1':{'d2':2}})
64+
def test_glue_keys(self):
65+
glue_keys({'d1': {'d2': 2}}, display=False)
6466

6567

6668
if __name__ == '__main__':

setup.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414

1515
pkgname = "aviary"
1616
extras_require = {
17-
"test": ["testflo", "pre-commit", "sphinx_book_theme==1.1.0"],
17+
"test": ["testflo", "pre-commit", "sphinx_book_theme==1.1.0", "myst-nb"],
1818
"examples": ["openaerostruct", "ambiance", "itables"],
1919
}
2020

0 commit comments

Comments
 (0)