modelflowib 2.79__tar.gz → 2.81__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (50) hide show
  1. {modelflowib-2.79 → modelflowib-2.81}/PKG-INFO +14 -10
  2. {modelflowib-2.79 → modelflowib-2.81}/modelclass.py +171 -65
  3. {modelflowib-2.79 → modelflowib-2.81}/modelflowib.egg-info/PKG-INFO +14 -10
  4. {modelflowib-2.79 → modelflowib-2.81}/modelflowib.egg-info/requires.txt +12 -6
  5. {modelflowib-2.79 → modelflowib-2.81}/pyproject.toml +23 -11
  6. {modelflowib-2.79 → modelflowib-2.81}/README.md +0 -0
  7. {modelflowib-2.79 → modelflowib-2.81}/license.md +0 -0
  8. {modelflowib-2.79 → modelflowib-2.81}/modelBLfunk.py +0 -0
  9. {modelflowib-2.79 → modelflowib-2.81}/model_Excel.py +0 -0
  10. {modelflowib-2.79 → modelflowib-2.81}/model_cvx.py +0 -0
  11. {modelflowib-2.79 → modelflowib-2.81}/model_dynare.py +0 -0
  12. {modelflowib-2.79 → modelflowib-2.81}/model_financial_stability.py +0 -0
  13. {modelflowib-2.79 → modelflowib-2.81}/model_latex.py +0 -0
  14. {modelflowib-2.79 → modelflowib-2.81}/model_latex_class.py +0 -0
  15. {modelflowib-2.79 → modelflowib-2.81}/model_parquet_mixin.py +0 -0
  16. {modelflowib-2.79 → modelflowib-2.81}/modelconstruct.py +0 -0
  17. {modelflowib-2.79 → modelflowib-2.81}/modelconstruct_estimation.py +0 -0
  18. {modelflowib-2.79 → modelflowib-2.81}/modeldash.py +0 -0
  19. {modelflowib-2.79 → modelflowib-2.81}/modeldashboot.py +0 -0
  20. {modelflowib-2.79 → modelflowib-2.81}/modeldashsidebar.py +0 -0
  21. {modelflowib-2.79 → modelflowib-2.81}/modeldekom.py +0 -0
  22. {modelflowib-2.79 → modelflowib-2.81}/modeldiff.py +0 -0
  23. {modelflowib-2.79 → modelflowib-2.81}/modeldisplay.py +0 -0
  24. {modelflowib-2.79 → modelflowib-2.81}/modelestimation.py +0 -0
  25. {modelflowib-2.79 → modelflowib-2.81}/modelestimator_new.py +0 -0
  26. {modelflowib-2.79 → modelflowib-2.81}/modelflowib.egg-info/SOURCES.txt +0 -0
  27. {modelflowib-2.79 → modelflowib-2.81}/modelflowib.egg-info/dependency_links.txt +0 -0
  28. {modelflowib-2.79 → modelflowib-2.81}/modelflowib.egg-info/top_level.txt +0 -0
  29. {modelflowib-2.79 → modelflowib-2.81}/modelgrab.py +0 -0
  30. {modelflowib-2.79 → modelflowib-2.81}/modelgrabgdx.py +0 -0
  31. {modelflowib-2.79 → modelflowib-2.81}/modelgrabwf2.py +0 -0
  32. {modelflowib-2.79 → modelflowib-2.81}/modelhelp.py +0 -0
  33. {modelflowib-2.79 → modelflowib-2.81}/modelhtml.py +0 -0
  34. {modelflowib-2.79 → modelflowib-2.81}/modelinvert.py +0 -0
  35. {modelflowib-2.79 → modelflowib-2.81}/modeljupyter.py +0 -0
  36. {modelflowib-2.79 → modelflowib-2.81}/modeljupytermagic.py +0 -0
  37. {modelflowib-2.79 → modelflowib-2.81}/modelmacrograb.py +0 -0
  38. {modelflowib-2.79 → modelflowib-2.81}/modelmanipulation.py +0 -0
  39. {modelflowib-2.79 → modelflowib-2.81}/modelmf.py +0 -0
  40. {modelflowib-2.79 → modelflowib-2.81}/modelnet.py +0 -0
  41. {modelflowib-2.79 → modelflowib-2.81}/modelnewton.py +0 -0
  42. {modelflowib-2.79 → modelflowib-2.81}/modelnormalize.py +0 -0
  43. {modelflowib-2.79 → modelflowib-2.81}/modelpattern.py +0 -0
  44. {modelflowib-2.79 → modelflowib-2.81}/modelreport.py +0 -0
  45. {modelflowib-2.79 → modelflowib-2.81}/modelsolver_ng.py +0 -0
  46. {modelflowib-2.79 → modelflowib-2.81}/modeluserfunk.py +0 -0
  47. {modelflowib-2.79 → modelflowib-2.81}/modelvis.py +0 -0
  48. {modelflowib-2.79 → modelflowib-2.81}/modelwidget.py +0 -0
  49. {modelflowib-2.79 → modelflowib-2.81}/modelwidget_input.py +0 -0
  50. {modelflowib-2.79 → modelflowib-2.81}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: modelflowib
3
- Version: 2.79
3
+ Version: 2.81
4
4
  Summary: A tool to solve and manage dynamic economic and financial models
5
5
  Author-email: Ib Hansen <Ib.Hansen.Iv@gmail.com>
6
6
  License-Expression: MIT
@@ -20,22 +20,26 @@ Requires-Dist: numpy
20
20
  Requires-Dist: networkx
21
21
  Requires-Dist: tqdm
22
22
  Requires-Dist: scipy
23
- Requires-Dist: numexpr
24
- Requires-Dist: bottleneck
25
23
  Requires-Dist: XlsxWriter
26
24
  Requires-Dist: pyarrow
27
25
  Requires-Dist: tabulate
28
26
  Requires-Dist: openpyxl
29
27
  Requires-Dist: xarray
30
- Requires-Dist: cvxopt
31
- Requires-Dist: numba
32
- Requires-Dist: dash
33
- Requires-Dist: dash-bootstrap-components
34
- Requires-Dist: dash-interactive-graphviz
35
- Requires-Dist: lmfit
36
- Requires-Dist: jupyter
28
+ Requires-Dist: jinja2
29
+ Requires-Dist: ipython
37
30
  Requires-Dist: ipywidgets
38
31
  Requires-Dist: ipydatagrid
32
+ Requires-Dist: lmfit
33
+ Requires-Dist: statsmodels
34
+ Requires-Dist: numba; sys_platform != "emscripten"
35
+ Requires-Dist: cvxopt; sys_platform != "emscripten"
36
+ Requires-Dist: numexpr; sys_platform != "emscripten"
37
+ Requires-Dist: bottleneck; sys_platform != "emscripten"
38
+ Requires-Dist: dash; sys_platform != "emscripten"
39
+ Requires-Dist: dash-bootstrap-components; sys_platform != "emscripten"
40
+ Requires-Dist: dash-interactive-graphviz; sys_platform != "emscripten"
41
+ Requires-Dist: plotly; sys_platform != "emscripten"
42
+ Requires-Dist: jupyter; sys_platform != "emscripten"
39
43
  Provides-Extra: shiny
40
44
  Requires-Dist: shiny; extra == "shiny"
41
45
  Requires-Dist: shinywidgets; extra == "shiny"
@@ -73,6 +73,11 @@ from typing import Iterable
73
73
  import zipfile
74
74
  from functools import partial,cached_property,lru_cache
75
75
  from tqdm.auto import tqdm
76
+ if sys.platform == 'emscripten':
77
+ # a browser has no threads, so tqdm can not start its monitor thread. Set on the
78
+ # base class, which tqdm.auto and tqdm.notebook inherit it from
79
+ import tqdm as tqdm_module
80
+ tqdm_module.tqdm.monitor_interval = 0
76
81
  import operator
77
82
  from urllib.request import urlopen
78
83
  from io import BytesIO,StringIO
@@ -3907,20 +3912,20 @@ class Graph_Draw_Mixin():
3907
3912
 
3908
3913
  def drawendo(self, **kwargs):
3909
3914
  '''draws a graph of of the whole model'''
3910
- alllinks = (node(0, n[1], n[0]) for n in self.endograph.edges())
3911
- return self.todot2(alllinks, **kwargs)
3915
+ alllinks = [node(0, n[1], n[0]) for n in self.endograph.edges()]
3916
+ return self.draw_links(alllinks, **kwargs)
3912
3917
 
3913
3918
  def drawendo_lag_lead(self, **kwargs):
3914
3919
  '''draws a graph of of the whole model'''
3915
- alllinks = (node(0, n[1], n[0])
3916
- for n in self.endograph_lag_lead.edges())
3917
- return self.todot2(alllinks, **kwargs)
3920
+ alllinks = [node(0, n[1], n[0])
3921
+ for n in self.endograph_lag_lead.edges()]
3922
+ return self.draw_links(alllinks, **kwargs)
3918
3923
 
3919
3924
  def drawmodel(self, lag=True, **kwargs):
3920
3925
  '''draws a graph of of the whole model'''
3921
3926
  graph = self.totgraph if lag else self.totgraph_nolag
3922
- alllinks = (node(0, n[1], n[0]) for n in graph.edges())
3923
- return self.todot2(alllinks, **kwargs)
3927
+ alllinks = [node(0, n[1], n[0]) for n in graph.edges()]
3928
+ return self.draw_links(alllinks, **kwargs)
3924
3929
 
3925
3930
  def plotadjacency(self, size=(5, 5), title='Structure', nolag=False):
3926
3931
  '''
@@ -3970,14 +3975,26 @@ class Graph_Draw_Mixin():
3970
3975
  self.upwalk(graph, navn.upper(), maxlevel=down, lpre=False,filter=filter))
3971
3976
  return list(chain(uplinks, downlinks))
3972
3977
 
3973
- def draw(self, navn, down=1, up=1, lag=False, endo=False, filter=0, engine='dot', **kwargs):
3978
+ # in a browser (jupyterlite) there is no graphviz program, and the svg drawing
3979
+ # is the one which can be zoomed and has tooltips
3980
+ if sys.platform == 'emscripten':
3981
+ #: the engine of draw: dot for graphviz, else an engine of draw_nx
3982
+ draw_engine = 'svg'
3983
+ #: the engine of draw_nx: mpl for matplotlib or svg
3984
+ nx_engine = 'svg'
3985
+ else:
3986
+ draw_engine = 'dot'
3987
+ nx_engine = 'mpl'
3988
+
3989
+ def draw(self, navn, down=1, up=1, lag=False, endo=False, filter=0, engine='', **kwargs):
3974
3990
  '''draws a graph of dependensies of navn up to maxlevel
3975
3991
 
3976
3992
  :lag: show the complete graph including lagged variables else only variables.
3977
3993
  :endo: Show only the graph for current endogenous variables
3978
3994
  :down: level downstream
3979
3995
  :up: level upstream
3980
- :engine: dot draws with graphviz, mpl and svg are the engines of draw_nx
3996
+ :engine: dot draws with graphviz, mpl and svg are the engines of draw_nx,
3997
+ default self.draw_engine which is svg in a browser else dot
3981
3998
 
3982
3999
  If graphviz fails - the dot program is for instance not there when running
3983
4000
  in a browser - or if self.no_graphviz is True, draw_nx draws the graph
@@ -3985,19 +4002,40 @@ class Graph_Draw_Mixin():
3985
4002
  '''
3986
4003
  alllinks = self.get_alllinks(navn, down=down, up=up, lag=lag, endo=endo,
3987
4004
  filter=filter)
3988
- # dot=True means the caller wants the dot source, for instance the dash apps,
3989
- # then no_graphviz is not respected, as networkx can not make a dot file
3990
- if engine == 'dot' and (not self.no_graphviz or kwargs.get('dot', False)):
4005
+ return self.draw_links(alllinks, navn=navn.upper(), engine=engine,
4006
+ down=down, up=up, filter=filter, **kwargs)
4007
+
4008
+ def draw_links(self, alllinks, navn='', engine='', **kwargs):
4009
+ '''Draws the links from get_alllinks, drawmodel, drawendo ... with the engine
4010
+
4011
+ :engine: dot draws with graphviz, mpl and svg are the engines of draw_nx,
4012
+ default self.draw_engine which is svg in a browser else dot
4013
+
4014
+ If graphviz fails - the dot program is for instance not there when running
4015
+ in a browser - or if self.no_graphviz is True, networkx draws the graph.
4016
+ '''
4017
+ if kwargs.get('dot', False):
4018
+ # the caller wants the dot source itself, for instance the dash apps.
4019
+ # The dot program is not needed for that, and networkx can not make it
4020
+ return self.todot2(alllinks, navn=navn, **kwargs)
4021
+
4022
+ engine = engine or self.draw_engine
4023
+ if engine == 'dot' and not self.no_graphviz:
3991
4024
  try:
3992
- return self.todot2(alllinks, navn=navn.upper(), down=down, up=up, filter = filter, **kwargs)
4025
+ return self.todot2(alllinks, navn=navn, **kwargs)
3993
4026
  except Exception as e:
3994
4027
  print(f'Graphviz did not draw the graph: {e}\nDrawing with networkx')
3995
- engine = 'mpl'
4028
+ engine = self.nx_engine
3996
4029
  elif engine == 'dot':
3997
- engine = 'mpl' # no_graphviz is set, then networkx draws the graph
4030
+ engine = self.nx_engine # no_graphviz is set, then networkx draws it
3998
4031
 
3999
- return self.draw_nx(navn, down=down, up=up, lag=lag, endo=endo, filter=filter,
4000
- alllinks=alllinks, engine=engine, **kwargs)
4032
+ if not len(alllinks):
4033
+ print(f'No graph {navn}')
4034
+ if kwargs.get('filter', 0):
4035
+ print('Perhaps filter prunes to much')
4036
+ return
4037
+ G = self.alllinks_to_nx(alllinks, navn=navn, **kwargs)
4038
+ return self.display_nx(G, navn=navn, engine=engine, **kwargs)
4001
4039
 
4002
4040
  #: Set to True on a model instance to make draw use draw_nx, so the networkx
4003
4041
  #: drawing can be tested on a machine where graphviz works
@@ -4085,18 +4123,26 @@ class Graph_Draw_Mixin():
4085
4123
  except Exception:
4086
4124
  G.nodes[v]['values'] = None
4087
4125
 
4088
- # the layer is the distance to navn, so the drawing can be layered like rankdir=LR
4126
+ # the layer places the node in a column, so the drawing is layered like rankdir=LR
4089
4127
  if navn in G:
4128
+ # the layer is the distance to navn, upstream to the left of it
4090
4129
  upstream = nx.single_source_shortest_path_length(G.reverse(copy=False), navn)
4091
4130
  downstream = nx.single_source_shortest_path_length(G, navn)
4131
+ for v in G.nodes:
4132
+ up_len, down_len = upstream.get(v), downstream.get(v)
4133
+ if up_len is not None and (down_len is None or up_len <= down_len):
4134
+ G.nodes[v]['layer'] = -up_len
4135
+ else:
4136
+ G.nodes[v]['layer'] = 0 if down_len is None else down_len
4092
4137
  else:
4093
- upstream, downstream = {}, {}
4094
- for v in G.nodes:
4095
- up_len, down_len = upstream.get(v), downstream.get(v)
4096
- if up_len is not None and (down_len is None or up_len <= down_len):
4097
- G.nodes[v]['layer'] = -up_len
4098
- else:
4099
- G.nodes[v]['layer'] = 0 if down_len is None else down_len
4138
+ # no variable in the center, like drawmodel, then the layer is the logical
4139
+ # order. The simultaneous blocks are condensed to one node, else there is
4140
+ # no order, and a node of such a block gets the layer of its block
4141
+ condensed = nx.condensation(G)
4142
+ for layer, blocks in enumerate(nx.topological_generations(condensed)):
4143
+ for block in blocks:
4144
+ for v in condensed.nodes[block]['members']:
4145
+ G.nodes[v]['layer'] = layer
4100
4146
 
4101
4147
  for child, parent in G.edges:
4102
4148
  try:
@@ -4166,23 +4212,21 @@ class Graph_Draw_Mixin():
4166
4212
  if alllinks is None:
4167
4213
  alllinks = self.get_alllinks(navn, down=down, up=up, lag=lag, endo=endo,
4168
4214
  filter=filter)
4169
- if not len(alllinks):
4170
- print(f'No graph {navn}')
4171
- if filter:
4172
- print('Perhaps filter prunes to much')
4173
- return
4174
- G = self.alllinks_to_nx(alllinks, navn=navn.upper(), **kwargs)
4175
- return self.display_nx(G, navn=navn.upper(), **kwargs)
4215
+ engine = kwargs.pop('engine', '') or self.nx_engine
4216
+ return self.draw_links(alllinks, navn=navn.upper(), engine=engine,
4217
+ down=down, up=up, filter=filter, **kwargs)
4176
4218
 
4177
- def display_nx(self, G, navn='', engine='mpl', **kwargs):
4219
+ def display_nx(self, G, navn='', engine='', **kwargs):
4178
4220
  '''Draws a graph made by alllinks_to_nx
4179
4221
 
4180
- :engine: mpl draws with matplotlib, svg makes a svg drawing
4222
+ :engine: mpl draws with matplotlib, svg makes a svg drawing,
4223
+ default self.nx_engine which is svg in a browser else mpl
4181
4224
 
4182
4225
  Each engine is the method display_nx_<engine>, so a new engine is made by
4183
4226
  writing a new method. They all take the same graph and the positions from
4184
4227
  nx_layout, and only do the drawing itself.
4185
4228
  '''
4229
+ engine = engine or self.nx_engine
4186
4230
  try:
4187
4231
  displayer = getattr(self, f'display_nx_{engine}')
4188
4232
  except AttributeError:
@@ -4330,7 +4374,12 @@ class Graph_Draw_Mixin():
4330
4374
  svgname.write_text(svg, encoding='utf-8')
4331
4375
  if browser:
4332
4376
  # the same as display_graph, a separate window where the svg can be zoomed
4333
- wb.open(f'file://{svgname.resolve()}', new=2)
4377
+ try:
4378
+ wb.open(f'file://{svgname.resolve()}', new=2)
4379
+ except Exception as e:
4380
+ # in a browser (jupyterlite) there is no window to open from
4381
+ print(f'No separate window here: {e}')
4382
+ print(f'The drawing is saved as {svgname} and is shown below')
4334
4383
 
4335
4384
  display(HTML(svg))
4336
4385
  self.last_svg = svg
@@ -6273,36 +6322,41 @@ class Display_Mixin():
6273
6322
  from IPython.display import display, Markdown, HTML
6274
6323
  from pathlib import Path
6275
6324
  display(Markdown(text))
6325
+ rows = []
6276
6326
  for dir in sorted(Path(folder).glob('**')):
6277
6327
  # print(f'{dir=} {nocwd=}')
6278
6328
  if len(dir.parts) and str(dir.parts[-1]).startswith('.'):
6279
6329
  continue
6280
-
6330
+
6281
6331
  if dir == Path('.') and nocwd :
6282
6332
  continue
6283
-
6284
- filelist = (list(dir.glob('*readme.ipynb'))
6285
- + [f for f in sorted(dir.glob('*.ipynb'))
6333
+
6334
+ filelist = (list(dir.glob('*readme.ipynb'))
6335
+ + [f for f in sorted(dir.glob('*.ipynb'))
6286
6336
  if not f.stem.endswith('readme')])
6287
-
6288
- for i, notebook in enumerate(filelist):
6289
- # print(notebook)
6290
- if (not all) and (notebook.name.startswith('test') or notebook.name.startswith('Overview')):
6291
- continue
6292
- if i == 0:
6293
- blanks = ''.join(
6294
- ['&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;']*len(dir.parts))
6295
- if len(dir.parts):
6296
- display(HTML(f'{blanks}<b>{str(dir)}</b>'))
6297
- else:
6298
- display(
6299
- HTML(f'{blanks}<b>{str(Path.cwd().parts[-1])} (.)</b>'))
6300
6337
 
6301
- name = notebook.name.split('.')[0]
6338
+ if not all:
6339
+ filelist = [f for f in filelist
6340
+ if not (f.name.startswith('test') or f.name.startswith('Overview'))]
6341
+
6342
+ if not filelist:
6343
+ continue
6344
+
6345
+ indent = 2 * len(dir.parts) # indentation of this folder in em
6346
+ folder_name = str(dir) if len(dir.parts) else f'{str(Path.cwd().parts[-1])} (.)'
6347
+ rows.append(
6348
+ f'<tr><td colspan="2" style="text-align:left;padding:6px 12px 2px {indent}em">'
6349
+ f'<b>{folder_name}</b></td></tr>')
6350
+
6351
+ for notebook in filelist:
6302
6352
  # print(notebook)
6303
- display(HTML(
6304
- f'&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;{blanks} <a href="{notebook}" target="_blank">{name}</a>'))
6305
-
6353
+ name = notebook.name.split('.')[0]
6354
+ title = Display_Mixin._notebook_title(notebook)
6355
+ rows.append(
6356
+ f'<tr><td style="text-align:left;padding:2px 24px 2px {indent+2}em;white-space:nowrap">'
6357
+ f'<a href="{notebook}" target="_blank">{name}</a></td>'
6358
+ f'<td style="text-align:left;padding:2px 12px">{title}</td></tr>')
6359
+
6306
6360
  # try:
6307
6361
  # # Replace with your method of opening a notebook
6308
6362
  # # Example: Using jupyter command-line to open the notebook
@@ -6310,12 +6364,43 @@ class Display_Mixin():
6310
6364
  # subprocess.Popen(["jupyter", "notebook", str(notebook)])
6311
6365
  # except Exception as e:
6312
6366
  # print(f"Error opening notebook {notebook}: {e}")
6367
+ if rows:
6368
+ display(HTML(
6369
+ '<table style="border-collapse:collapse;border:none">'
6370
+ + ''.join(rows) + '</table>'))
6313
6371
 
6314
6372
 
6373
+ @staticmethod
6374
+ def _notebook_title(fname):
6375
+ '''Returns the first line starting with one or more # in the first cell of a jupyter notebook.
6376
+
6377
+ Returns an empty string if the notebook can not be read or no such line is found'''
6378
+
6379
+ import json
6380
+ from html import escape
6381
+
6382
+ try:
6383
+ with open(fname, 'rt', encoding='utf-8') as f:
6384
+ nb = json.load(f)
6385
+ cells = nb.get('cells', [])
6386
+ if not cells:
6387
+ return ''
6388
+ source = cells[0].get('source', '')
6389
+ lines = source.splitlines() if isinstance(source, str) else source
6390
+ for line in lines:
6391
+ stripped = line.strip()
6392
+ if stripped.startswith('#'):
6393
+ return escape(stripped.lstrip('#').strip())
6394
+ except Exception:
6395
+ ...
6396
+ return ''
6397
+
6315
6398
  @staticmethod
6316
6399
  def display_toc_this(pat='*',text='**Jupyter notebooks**',path='.',ext='ipynb',showext=False):
6317
-
6318
- '''In a jupyter notebook this function displays a clickable table of content in the folder pat with name in path'''
6400
+
6401
+ '''In a jupyter notebook this function displays a clickable table of content in the folder pat with name in path
6402
+
6403
+ For notebooks the first markdown header in the first cell is shown beside the name'''
6319
6404
 
6320
6405
  from IPython.display import display, Markdown, HTML
6321
6406
  from pathlib import Path
@@ -6323,10 +6408,18 @@ class Display_Mixin():
6323
6408
  display(Markdown(text))
6324
6409
  dir = Path(path)
6325
6410
  # print(dir,':')
6411
+ rows = []
6326
6412
  for fname in sorted(dir.glob(pat+'.'+ext)):
6327
6413
  name = fname.name if showext else fname.name.split('.')[0]
6414
+ title = Display_Mixin._notebook_title(fname) if ext == 'ipynb' else ''
6415
+ rows.append(
6416
+ f'<tr><td style="text-align:left;padding:2px 24px 2px 12px;white-space:nowrap">'
6417
+ f'<a href="{fname}" target="_blank">{name}</a></td>'
6418
+ f'<td style="text-align:left;padding:2px 12px">{title}</td></tr>')
6419
+ if rows:
6328
6420
  display(HTML(
6329
- f'&nbsp; &nbsp; <a href="{fname}" target="_blank">{name}</a>'))
6421
+ '<table style="border-collapse:collapse;border:none">'
6422
+ + ''.join(rows) + '</table>'))
6330
6423
 
6331
6424
 
6332
6425
  @staticmethod
@@ -6379,13 +6472,25 @@ class Display_Mixin():
6379
6472
  } catch (e) { /* ignore */ }
6380
6473
  """))
6381
6474
 
6382
- # Notebook 7 / JupyterLab: remove the scrolled-output max-height
6383
- # and the scrolled modifier wherever it is applied.
6475
+ # Notebook 7 / JupyterLab: the height is set on the output area of the
6476
+ # cell: .jp-mod-outputsScrolled .jp-Cell-outputArea {max-height: 24em}
6477
+ # height:auto also undoes a height the user has dragged in place, as an
6478
+ # important declaration wins over an inline style
6384
6479
  display(HTML("""
6385
6480
  <style id="mf-scroll-off">
6481
+ .jp-mod-outputsScrolled .jp-Cell-outputArea {
6482
+ max-height: none !important;
6483
+ height: auto !important;
6484
+ overflow-y: visible !important;
6485
+ resize: none !important;
6486
+ }
6386
6487
  .jp-OutputArea-output.jp-OutputArea-output { max-height: none !important; }
6387
- .jp-mod-outputsScrolled .jp-OutputArea-child {
6488
+ /* Classic Notebook (<=6) sets height, not max-height, on output_scroll */
6489
+ .output_scroll {
6490
+ height: auto !important;
6388
6491
  max-height: none !important;
6492
+ overflow: visible !important;
6493
+ box-shadow: none !important;
6389
6494
  }
6390
6495
  </style>
6391
6496
  """))
@@ -6410,11 +6515,12 @@ class Display_Mixin():
6410
6515
  } catch (e) { /* ignore */ }
6411
6516
  """))
6412
6517
 
6413
- # Remove the override stylesheet we injected above, if present.
6518
+ # Remove the override stylesheets we injected above, there is one for
6519
+ # each cell where scroll_off has been run
6414
6520
  display(Javascript("""
6415
6521
  try {
6416
- var s = document.getElementById('mf-scroll-off');
6417
- if (s) { s.parentNode.removeChild(s); }
6522
+ document.querySelectorAll('#mf-scroll-off').forEach(
6523
+ function (s) { s.parentNode.removeChild(s); });
6418
6524
  } catch (e) { /* ignore */ }
6419
6525
  """))
6420
6526
  except Exception as e:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: modelflowib
3
- Version: 2.79
3
+ Version: 2.81
4
4
  Summary: A tool to solve and manage dynamic economic and financial models
5
5
  Author-email: Ib Hansen <Ib.Hansen.Iv@gmail.com>
6
6
  License-Expression: MIT
@@ -20,22 +20,26 @@ Requires-Dist: numpy
20
20
  Requires-Dist: networkx
21
21
  Requires-Dist: tqdm
22
22
  Requires-Dist: scipy
23
- Requires-Dist: numexpr
24
- Requires-Dist: bottleneck
25
23
  Requires-Dist: XlsxWriter
26
24
  Requires-Dist: pyarrow
27
25
  Requires-Dist: tabulate
28
26
  Requires-Dist: openpyxl
29
27
  Requires-Dist: xarray
30
- Requires-Dist: cvxopt
31
- Requires-Dist: numba
32
- Requires-Dist: dash
33
- Requires-Dist: dash-bootstrap-components
34
- Requires-Dist: dash-interactive-graphviz
35
- Requires-Dist: lmfit
36
- Requires-Dist: jupyter
28
+ Requires-Dist: jinja2
29
+ Requires-Dist: ipython
37
30
  Requires-Dist: ipywidgets
38
31
  Requires-Dist: ipydatagrid
32
+ Requires-Dist: lmfit
33
+ Requires-Dist: statsmodels
34
+ Requires-Dist: numba; sys_platform != "emscripten"
35
+ Requires-Dist: cvxopt; sys_platform != "emscripten"
36
+ Requires-Dist: numexpr; sys_platform != "emscripten"
37
+ Requires-Dist: bottleneck; sys_platform != "emscripten"
38
+ Requires-Dist: dash; sys_platform != "emscripten"
39
+ Requires-Dist: dash-bootstrap-components; sys_platform != "emscripten"
40
+ Requires-Dist: dash-interactive-graphviz; sys_platform != "emscripten"
41
+ Requires-Dist: plotly; sys_platform != "emscripten"
42
+ Requires-Dist: jupyter; sys_platform != "emscripten"
39
43
  Provides-Extra: shiny
40
44
  Requires-Dist: shiny; extra == "shiny"
41
45
  Requires-Dist: shinywidgets; extra == "shiny"
@@ -6,22 +6,28 @@ numpy
6
6
  networkx
7
7
  tqdm
8
8
  scipy
9
- numexpr
10
- bottleneck
11
9
  XlsxWriter
12
10
  pyarrow
13
11
  tabulate
14
12
  openpyxl
15
13
  xarray
16
- cvxopt
14
+ jinja2
15
+ ipython
16
+ ipywidgets
17
+ ipydatagrid
18
+ lmfit
19
+ statsmodels
20
+
21
+ [:sys_platform != "emscripten"]
17
22
  numba
23
+ cvxopt
24
+ numexpr
25
+ bottleneck
18
26
  dash
19
27
  dash-bootstrap-components
20
28
  dash-interactive-graphviz
21
- lmfit
29
+ plotly
22
30
  jupyter
23
- ipywidgets
24
- ipydatagrid
25
31
 
26
32
  [shiny]
27
33
  shiny
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "modelflowib" # PyPI distribution name
7
- version = "2.79" # OVERWRITTEN at build time from MF_VERSION
7
+ version = "2.81" # OVERWRITTEN at build time from MF_VERSION
8
8
  # (set_pyproject_version.py / make_pip.py -v)
9
9
  description = "A tool to solve and manage dynamic economic and financial models"
10
10
  readme = "README.md"
@@ -18,8 +18,15 @@ classifiers = [
18
18
  "Operating System :: OS Independent",
19
19
  ]
20
20
 
21
- # PyPI dependencies (pip names). Carried over from the previous make_pip.py.
21
+ # PyPI dependencies (pip names).
22
+ #
23
+ # The ones marked `sys_platform != "emscripten"` have no wasm build, or need a web
24
+ # server, so they can not be installed in a browser (JupyterLite/Pyodide). pip on a
25
+ # desktop installs them as before; micropip in the browser skips them. That makes
26
+ # `piplite.install("modelflowib")` work *with* its dependencies, so a notebook in
27
+ # JupyterLite needs no list of packages of its own.
22
28
  dependencies = [
29
+ # --- everywhere ---
23
30
  "pandas",
24
31
  "matplotlib",
25
32
  "seaborn",
@@ -28,22 +35,27 @@ dependencies = [
28
35
  "networkx",
29
36
  "tqdm",
30
37
  "scipy",
31
- "numexpr",
32
- "bottleneck",
33
38
  "XlsxWriter",
34
39
  "pyarrow",
35
40
  "tabulate",
36
41
  "openpyxl",
37
42
  "xarray",
38
- "cvxopt",
39
- "numba",
40
- "dash",
41
- "dash-bootstrap-components",
42
- "dash-interactive-graphviz",
43
- "lmfit",
44
- "jupyter",
43
+ "jinja2", # pandas .style, used by the report and vis tables
44
+ "ipython", # IPython.display, imported by most modules
45
45
  "ipywidgets",
46
46
  "ipydatagrid",
47
+ "lmfit", # modelestimation, modelestimator_new
48
+ "statsmodels", # modelestimation, modelestimator_new
49
+ # --- not in a browser ---
50
+ "numba; sys_platform != 'emscripten'",
51
+ "cvxopt; sys_platform != 'emscripten'",
52
+ "numexpr; sys_platform != 'emscripten'", # pandas accelerators
53
+ "bottleneck; sys_platform != 'emscripten'",
54
+ "dash; sys_platform != 'emscripten'", # needs a web server
55
+ "dash-bootstrap-components; sys_platform != 'emscripten'",
56
+ "dash-interactive-graphviz; sys_platform != 'emscripten'",
57
+ "plotly; sys_platform != 'emscripten'", # modeldashsidebar
58
+ "jupyter; sys_platform != 'emscripten'", # JupyterLite is the notebook there
47
59
  ]
48
60
 
49
61
  [project.optional-dependencies]
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes