--------------------------------------------------------------------------------
# evaluation

Evaluating Python expressions in Pobshell

Commands like 'eval' and 'printpy' evaluate Python expressions in object namespaces.
* Add objects to root namespace with '::import foo'

* Filter object members using '--matchpy PYEXPR' with introspection commands
* Filter search results using '--matchpy PYEXPR' in 'find'
  - Then evaluate expressions on matches using '--printpy PYEXPR'
* Evaluate a Python expression for a specific object; no trailing /
    ▶ printpy PYEXPR /foo/bar
* Evaluate a Python expression for members of an object; use trailing /
    ▶ printpy PYEXPR /foo/bar/
* Evaluate a Python expression for each member of current object; no TARGET
    ▶ printpy PYEXPR 

## PYEXPR evaluation namespace (locals & globals)
* locals are the members (usually attributes) of the target object
* Two names are added to locals
  - 'self' -- the target Python object 
  - 'pn'   -- the PobNode (Pobshell wrapper) representing Pobshell path and obj
* By default globals is just Pobshell's root namespace, c.f. 'ls /'


## Modes
- ':' evaluates in the current object; it's alias for eval command
- '::' evaluates in root (membership changes are persistent)
- '--matchpy' for an introspection command evaluates at TARGET or its members
  introspection command only operates on members having PYEXPR is True
- '--printpy' OPTION for 'find' command evaluates PYEXPR on each object matched
- 'printpy' command outputs PYEXPR evaluated at TARGET object or its members
  Syntax is 'printpy PYEXPR [TARGET]'  
  TARGET with trailing / means evaluate for each member of TARGET object

## Tips
* Adding objects to root allows PYEXPR to reference them
  N.B. Assuming you're using default setting 'set global_ns /' 


## Example #1
E.g. Add numpy to root namespace and invoke np.mean on each member
  N.B. Assumes 'map contents' and 'set global_ns /'
  /iris/data ▶ ::import numpy as np
  /iris/data ▶ printpy "np.mean(self)" -1
  `0`    2.55
  `1`    2.375
  `2`    2.35
  `3`    2.3499999999999996
  [...]

## Other evaluation examples
  /foo ▶ :len(self)
  /foo ▶ find . --matchpy "len(self) > 100" --noraise
  /foo ▶ find . --printpy "pn.abspath" --typename list
  /foo ▶ printpy -1 "self[-1]" --noraise 


## DEMONSTRATION OF A CHANGE PERSISTING IN ROOT BUT NOT PASSED BACK TO CALLING SCOPE
| => ipython
Python 3.12.8 | packaged by conda-forge | (main, Dec  5 2024, 14:25:12) [Clang 18.1.8 ]
Type 'copyright', 'credits' or 'license' for more information
IPython 8.30.0 -- An enhanced Interactive Python. Type '?' for help.

In [1]: x = 'jfksfjks'

In [2]: import pobshell; pobshell.shell()

ɘmoɔlɘW to pobshell. Type 'help' for instructions; 'quit' to exit 
2025-05-19 11:56:08.164887
/ ▶ ls
In  Out  exit  get_ipython  open  quit  x
/ ▶ ::x = 42
/ ▶ ls
In  Out  exit  get_ipython  open  quit  x
/ ▶ ls -l
In           list                      ['', "x = 'jfksfjks'", 'import pobshell; pobshell.shell()']
Out          dict                      {}
exit         ExitAutocall              <IPython.core.autocall.ExitAutocall object at 0x10576a750>
get_ipython  method                    <bound method InteractiveShell.get_ipython of <IPython.terminal.interactiveshell…
open         function                  <function open at 0x1045a8f40>
quit         ExitAutocall              <IPython.core.autocall.ExitAutocall object at 0x10576a750>
x            int                       42
/ ▶ quit

In [3]: x
Out[3]: 'jfksfjks'


## DEMONSTRATION OF A MUTATION IN ROOT PASSED BACK TO CALLING SCOPE
| => ipython
Python 3.12.8 | packaged by conda-forge | (main, Dec  5 2024, 14:25:12) [Clang 18.1.8 ]
Type 'copyright', 'credits' or 'license' for more information
IPython 8.30.0 -- An enhanced Interactive Python. Type '?' for help.

In [1]: l = ['we', 'interrupt', 'this', 'program', 'to']

In [2]: import pobshell; pobshell.shell()

ɘmoɔlɘW to pobshell. Type 'help' for instructions; 'quit' to exit 
2025-05-19 12:01:08.463072
/ ▶ ls
In  Out  exit  get_ipython  l  open  quit
/ ▶ ls -l
In           list                      ['', "l = ['we', 'interrupt', 'this', 'program', 'to']", 'import pobshell; pobsh…
Out          dict                      {}
exit         ExitAutocall              <IPython.core.autocall.ExitAutocall object at 0x108236870>
get_ipython  method                    <bound method InteractiveShell.get_ipython of <IPython.terminal.interactiveshell…
l            list                      ['we', 'interrupt', 'this', 'program', 'to']
open         function                  <function open at 0x107804f40>
quit         ExitAutocall              <IPython.core.autocall.ExitAutocall object at 0x108236870>
/ ▶ ::l.append('help with debugging')
None
/ ▶ quit

In [3]: l
Out[3]: ['we', 'interrupt', 'this', 'program', 'to', 'help with debugging']


---