Skip to content

Commit 0caf161

Browse files
authored
Merge branch 'main' into glue_getting_started
2 parents f009e7e + d4cc805 commit 0caf161

File tree

5 files changed

+114
-33
lines changed

5 files changed

+114
-33
lines changed

aviary/docs/developer_guide/doctape.ipynb

Lines changed: 0 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -92,10 +92,6 @@
9292
" doctape.glue_variable(key, md_code=True)\n",
9393
" class_list += f'- `{key}` {val}\\n'\n",
9494
"\n",
95-
"# testing_list = ''\n",
96-
"# for key,val in testing_functions.items():\n",
97-
"# testing_list += f'- `{key}` {val}\\n'\n",
98-
"\n",
9995
"utility_list = '```{eval-rst}\\n'\n",
10096
"for key in utility_functions:\n",
10197
" doctape.glue_variable(key, md_code=True)\n",
@@ -117,19 +113,6 @@
117113
" doctape.glue_variable(key, md_code=True)\n",
118114
" testing_list += ' '*4+f'.. autofunction:: aviary.utils.doctape.{key}\\n{\" \"*8}:noindex:\\n\\n'\n",
119115
"testing_list += '```'\n",
120-
"# testing_list += '\\n\\n</details>'\n",
121-
"\n",
122-
"# glue_list = ''\n",
123-
"# for key,val in glue_functions.items():\n",
124-
"# glue_list += f'- `{key}` {key}\\n'\n",
125-
"\n",
126-
"# glue_list = ''\n",
127-
"# for key in glue_functions:\n",
128-
"# # doc_str = inspect.getdoc(imported_functions[key])\n",
129-
"# doc_str = imported_functions[key].__doc__.split('\\n')[1]\n",
130-
"# # doc_str = '\\n'.join([s+' ' for s in imported_functions[key].__doc__.split('\\n')])\n",
131-
"# print(doc_str)\n",
132-
"# glue_list += f'- `{key}`: {doc_str}\\n'\n",
133116
"\n",
134117
"glue_list = '```{eval-rst}\\n'\n",
135118
"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
@@ -408,12 +408,12 @@
408408
"outputs": [],
409409
"source": [
410410
"# Testing Cell\n",
411-
"from aviary.api import Mission\n",
411+
"from aviary.api import Aircraft\n",
412412
"from aviary.utils.doctape import glue_variable, get_previous_line, get_variable_name\n",
413413
"\n",
414-
"glue_variable('value', Mission.Design.MACH, md_code=True)\n",
414+
"glue_variable('value', Aircraft.Design.EMPTY_MASS, md_code=True)\n",
415415
"glue_variable('var_value_code', get_previous_line(), md_code=True)\n",
416-
"glue_variable(get_variable_name(Mission.Design.MACH), md_code=True)\n",
416+
"glue_variable(get_variable_name(Aircraft.Design.EMPTY_MASS), md_code=True)\n",
417417
"glue_variable('var_name_code', get_previous_line(), md_code=True)\n"
418418
]
419419
},
@@ -424,7 +424,7 @@
424424
"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",
425425
"\n",
426426
"For example:\n",
427-
"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",
427+
"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",
428428
"\n",
429429
"### {glue:md}`get_attribute_name`\n",
430430
"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."
@@ -483,6 +483,71 @@
483483
"p1_alt = get_value(simplified_dict, 'phase1.altitude.val')\n",
484484
"print(p1_alt)"
485485
]
486+
},
487+
{
488+
"cell_type": "code",
489+
"execution_count": null,
490+
"metadata": {
491+
"tags": [
492+
"remove-cell"
493+
]
494+
},
495+
"outputs": [],
496+
"source": [
497+
"# Testing Cell\n",
498+
"from aviary.utils.doctape import glue_variable, check_args, get_all_keys, get_previous_line\n",
499+
"from aviary.api import Aircraft, Mission\n",
500+
"\n",
501+
"glue_variable(Aircraft.__name__)\n",
502+
"glue_variable(Mission.__name__)\n",
503+
"\n",
504+
"track_layers = 'track_layers'\n",
505+
"check_args(get_all_keys, track_layers)\n",
506+
"glue_variable(track_layers)\n",
507+
"\n",
508+
"get_all_keys(Mission, track_layers='Mission')\n",
509+
"track_layers_with_name = get_previous_line().split(', ')[1].split(')')[0]\n",
510+
"glue_variable('track_layers_with_Mission', track_layers_with_name, display=False)\n"
511+
]
512+
},
513+
{
514+
"cell_type": "markdown",
515+
"metadata": {},
516+
"source": [
517+
"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"
518+
]
519+
},
520+
{
521+
"cell_type": "code",
522+
"execution_count": null,
523+
"metadata": {},
524+
"outputs": [],
525+
"source": [
526+
"from aviary.utils.doctape import get_all_keys, get_value, glue_keys\n",
527+
"from aviary.api import Mission\n",
528+
"\n",
529+
"k1=get_all_keys(Mission)\n",
530+
"print(k1[:5]) # Display the first 5 keys in Mission\n",
531+
"k2=get_all_keys(Mission, track_layers=True)\n",
532+
"print(k2[:5]) # Display the first 5 keys in Mission\n",
533+
"k3=get_all_keys(Mission, track_layers='Mission')\n",
534+
"print(k3[:5]) # Display the first 5 keys in Mission\n",
535+
"\n",
536+
"glue_keys(Mission, False)\n",
537+
"\n",
538+
"print(get_value(Mission,'Constraints.GEARBOX_SHAFT_POWER_RESIDUAL'))\n"
539+
]
540+
},
541+
{
542+
"cell_type": "markdown",
543+
"metadata": {},
544+
"source": [
545+
"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",
546+
"\n",
547+
"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",
548+
"\n",
549+
"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`."
550+
]
486551
}
487552
],
488553
"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)