phi.api module
from .python_builder import P Then0 = P.Then0 Then = P.Then Then1 = P.Then1 Then2 = P.Then2 Then3 = P.Then3 Then4 = P.Then4 Then5 = P.Then5 ThenAt = P.ThenAt Read = P.Read Write = P.Write Val = P.Val Pipe = P.Pipe Obj = P.Obj Rec = P.Rec Context = P.Context With = P.With Ref = P.Ref Dict = P.Dict F = P.F ReadList = P.ReadList List = P.List Seq = P.Seq If = P.If
Module variables
var Obj
var Read
var Rec
var Ref
Functions
def Context(
*args)
Builder Core. Also available as a global function as phi.Context
.
Returns the context object of the current dsl.With
statemente.
Arguments
- *args: By design
Context
accepts any number of arguments and completely ignores them.
This is a classmethod and it doesnt return a Builder
/Expression
by design so it can be called directly:
from phi import P, Context, Obj def read_file(z): f = Context() return f.read() lines = P.Pipe( "text.txt", P.With( open, read_file, Obj.split("\n") ) )
Here we called Context
with no arguments to get the context back, however, since you can also give this function an argument (which it will ignore) it can be passed to the DSL so we can rewrite the previous as:
from phi import P, Context, Obj lines = P.Pipe( "text.txt", P.With( open, Context, # f Obj.read() Obj.split("\n") ) )
Context
yields an exception when used outside of a With
block.
Also see
phi.builder.Builder.Obj
- dsl
@staticmethod def Context(*args): """ ilder Core**. Also available as a global function as `phi.Context`. rns the context object of the current `dsl.With` statemente. guments** *args**: By design `Context` accepts any number of arguments and completely ignores them. is a classmethod and it doesnt return a `Builder`/`Expression` by design so it can be called directly: from phi import P, Context, Obj def read_file(z): f = Context() return f.read() lines = P.Pipe( "text.txt", P.With( open, read_file, Obj.split("\\n") ) ) we called `Context` with no arguments to get the context back, however, since you can also give this function an argument (which it will ignore) it can be passed to the DSL so we can rewrite the previous as: from phi import P, Context, Obj lines = P.Pipe( "text.txt", P.With( open, Context, # f Obj.read() Obj.split("\\n") ) ) text` yields an exception when used outside of a `With` block. so see** hi.builder.Builder.Obj` sl](https://cgarciae.github.io/phi/dsl.m.html) """ if _WithContextManager.WITH_GLOBAL_CONTEXT is utils.NO_VALUE: raise Exception("Cannot use 'Context' outside of a 'With' block") return _WithContextManager.WITH_GLOBAL_CONTEXT
def Dict(
self, **branches)
def Dict(self, **branches): gs = { key : _parse(value)._f for key, value in branches.items() } def h(x, state): ys = {} for key, g in gs.items(): y, state = g(x, state) ys[key] = y return _RecordObject(**ys), state return self.__then__(h)
def F(
self, expr)
def F(self, expr): return self >> expr
def If(
self, condition, *then, **kwargs)
If
If(Predicate, *Then)
Having conditionals expressions a necesity in every language, Phi includes the If
expression for such a purpose.
Arguments
- Predicate : a predicate expression uses to determine if the
Then
orElse
branches should be used. - *Then : an expression to be excecuted if the
Predicate
yieldsTrue
, since this parameter is variadic you can stack expression and they will be interpreted as a tuplephi.dsl.Seq
.
This class also includes the Elif
and Else
methods which let you write branched conditionals in sequence, however the following rules apply
- If no branch is entered the whole expression behaves like the identity
Elif
can only be used after anIf
or anotherElif
expression- Many
Elif
expressions can be stacked sequentially Else
can only be used after anIf
orElif
expression
Examples
from phi import P, If assert "Between 2 and 10" == P.Pipe( 5, If(P > 10, "Greater than 10" ).Elif(P < 2, "Less than 2" ).Else( "Between 2 and 10" ) )
def If(self, condition, *then, **kwargs): """ ** If(Predicate, *Then) ng conditionals expressions a necesity in every language, Phi includes the `If` expression for such a purpose. guments** Predicate** : a predicate expression uses to determine if the `Then` or `Else` branches should be used. *Then** : an expression to be excecuted if the `Predicate` yields `True`, since this parameter is variadic you can stack expression and they will be interpreted as a tuple `phi.dsl.Seq`. class also includes the `Elif` and `Else` methods which let you write branched conditionals in sequence, however the following rules apply no branch is entered the whole expression behaves like the identity lif` can only be used after an `If` or another `Elif` expression ny `Elif` expressions can be stacked sequentially lse` can only be used after an `If` or `Elif` expression xamples ** from phi import P, If assert "Between 2 and 10" == P.Pipe( 5, If(P > 10, "Greater than 10" ).Elif(P < 2, "Less than 2" ).Else( "Between 2 and 10" ) ) """ cond_f = _parse(condition)._f then_f = E.Seq(*then)._f else_f = utils.state_identity ast = (cond_f, then_f, else_f) g = _compile_if(ast) expr = self.__then__(g, **kwargs) expr._ast = ast expr._root = self return expr
def List(
self, *branches, **kwargs)
While Seq
is sequential, phi.dsl.Expression.List
allows you to split the computation and get back a list with the result of each path. While the list literal should be the most incarnation of this expresion, it can actually be any iterable (implements __iter__
) that is not a tuple and yields a valid expresion.
The expression
k = List(f, g)
is equivalent to
k = lambda x: [ f(x), g(x) ]
In general, the following rules apply after compilation:
General Branching
List(f0, f1, ..., fn)
is equivalent to
lambda x: [ f0(x), f1(x), ..., fn(x) ]
Composing & Branching
It is interesting to see how braching interacts with composing. The expression
Seq(f, List(g, h))
is almost equivalent to
List( Seq(f, g), Seq(f, h) )
As you see its as if f
where distributed over the List. We say almost because their implementation is different
def _lambda(x): x = f(x) return [ g(x), h(x) ]
vs
lambda x: [ g(f(x)), h(f(x)) ]
As you see f
is only executed once in the first one. Both should yield the same result if f
is a pure function.
Examples
form phi import P, List avg_word_length = P.Pipe( "1 22 333", lambda s: s.split(' '), # ['1', '22', '333'] lambda l: map(len, l), # [1, 2, 3] List( sum # 1 + 2 + 3 == 6 , len # len([1, 2, 3]) == 3 ), lambda l: l[0] / l[1] # sum / len == 6 / 3 == 2 ) assert avg_word_length == 2
The previous could also be done more briefly like this
form phi import P, Obj, List avg_word_length = P.Pipe( "1 22 333", Obj .split(' ') # ['1', '22', '333'] .map(len) # [1, 2, 3] .List( sum #sum([1, 2, 3]) == 6 , len #len([1, 2, 3]) == 3 ), P[0] / P[1] #6 / 3 == 2 ) assert avg_word_length == 2
In the example above the last expression
P[0] / P[1]
works for a couple of reasons
- The previous expression returns a list
- In general the expression
P[x]
compiles to a function with the formlambda obj: obj[x]
-
The class
Expression
(the class from which the objectP
inherits) overrides most operators to create functions easily. For example, the expression(P * 2) / (P + 1)
compile to a function of the form
lambda x: (x * 2) / (x + 1)
Check out the documentatio for Phi lambdas.
def List(self, *branches, **kwargs): """ e `Seq` is sequential, `phi.dsl.Expression.List` allows you to split the computation and get back a list with the result of each path. While the list literal should be the most incarnation of this expresion, it can actually be any iterable (implements `__iter__`) that is not a tuple and yields a valid expresion. expression k = List(f, g) quivalent to k = lambda x: [ f(x), g(x) ] eneral, the following rules apply after compilation: neral Branching** List(f0, f1, ..., fn) quivalent to lambda x: [ f0(x), f1(x), ..., fn(x) ] mposing & Branching** s interesting to see how braching interacts with composing. The expression Seq(f, List(g, h)) almost* equivalent to List( Seq(f, g), Seq(f, h) ) ou see its as if `f` where distributed over the List. We say *almost* because their implementation is different def _lambda(x): x = f(x) return [ g(x), h(x) ] lambda x: [ g(f(x)), h(f(x)) ] ou see `f` is only executed once in the first one. Both should yield the same result if `f` is a pure function. Examples form phi import P, List avg_word_length = P.Pipe( "1 22 333", lambda s: s.split(' '), # ['1', '22', '333'] lambda l: map(len, l), # [1, 2, 3] List( sum # 1 + 2 + 3 == 6 , len # len([1, 2, 3]) == 3 ), lambda l: l[0] / l[1] # sum / len == 6 / 3 == 2 ) assert avg_word_length == 2 previous could also be done more briefly like this form phi import P, Obj, List avg_word_length = P.Pipe( "1 22 333", Obj .split(' ') # ['1', '22', '333'] .map(len) # [1, 2, 3] .List( sum #sum([1, 2, 3]) == 6 , len #len([1, 2, 3]) == 3 ), P[0] / P[1] #6 / 3 == 2 ) assert avg_word_length == 2 he example above the last expression P[0] / P[1] s for a couple of reasons he previous expression returns a list n general the expression `P[x]` compiles to a function with the form `lambda obj: obj[x]` he class `Expression` (the class from which the object `P` inherits) overrides most operators to create functions easily. For example, the expression (P * 2) / (P + 1) ile to a function of the form lambda x: (x * 2) / (x + 1) k out the documentatio for Phi [lambdas](https://cgarciae.github.io/phi/lambdas.m.html). """ gs = [ _parse(code)._f for code in branches ] def h(x, state): ys = [] for g in gs: y, state = g(x, state) ys.append(y) return (ys, state) return self.__then__(h, **kwargs)
def Pipe(
self, *sequence, **kwargs)
Pipe
runs any phi.dsl.Expression
. Its highly inspired by Elixir's |> (pipe) operator.
Arguments
- *sequence: any variable amount of expressions. All expressions inside of
sequence
will be composed together usingphi.dsl.Expression.Seq
. - **kwargs:
Pipe
forwards allkwargs
tophi.builder.Builder.Seq
, visit its documentation for more info.
The expression
Pipe(*sequence, **kwargs)
is equivalent to
Seq(*sequence, **kwargs)(None)
Normally the first argument or Pipe
is a value, that is reinterpreted as a phi.dsl.Expression.Val
, therfore, the input None
is discarded.
Examples
from phi import P def add1(x): return x + 1 def mul3(x): return x * 3 x = P.Pipe( 1, #input add1, #1 + 1 == 2 mul3 #2 * 3 == 6 ) assert x == 6
The previous using lambdas to create the functions
from phi import P x = P.Pipe( 1, #input P + 1, #1 + 1 == 2 P * 3 #2 * 3 == 6 ) assert x == 6
Also see
def Pipe(self, *sequence, **kwargs): """ e` runs any `phi.dsl.Expression`. Its highly inspired by Elixir's [|> (pipe)](https://hexdocs.pm/elixir/Kernel.html#%7C%3E/2) operator. guments** *sequence**: any variable amount of expressions. All expressions inside of `sequence` will be composed together using `phi.dsl.Expression.Seq`. **kwargs**: `Pipe` forwards all `kwargs` to `phi.builder.Builder.Seq`, visit its documentation for more info. expression Pipe(*sequence, **kwargs) quivalent to Seq(*sequence, **kwargs)(None) ally the first argument or `Pipe` is a value, that is reinterpreted as a `phi.dsl.Expression.Val`, therfore, the input `None` is discarded. amples** from phi import P def add1(x): return x + 1 def mul3(x): return x * 3 x = P.Pipe( 1, #input add1, #1 + 1 == 2 mul3 #2 * 3 == 6 ) assert x == 6 previous using [lambdas](https://cgarciae.github.io/phi/lambdas.m.html) to create the functions from phi import P x = P.Pipe( 1, #input P + 1, #1 + 1 == 2 P * 3 #2 * 3 == 6 ) assert x == 6 so see** hi.builder.Builder.Seq` sl](https://cgarciae.github.io/phi/dsl.m.html) ompile](https://cgarciae.github.io/phi/dsl.m.html#phi.dsl.Compile) ambdas](https://cgarciae.github.io/phi/lambdas.m.html) """ state = kwargs.pop("refs", {}) return self.Seq(*sequence, **kwargs)(None, **state)
def ReadList(
self, *branches, **kwargs)
Same as phi.dsl.Expression.List
but any string argument x
is translated to Read(x)
.
def ReadList(self, *branches, **kwargs): """ as `phi.dsl.Expression.List` but any string argument `x` is translated to `Read(x)`. """ branches = map(lambda x: E.Read(x) if isinstance(x, str) else x, branches) return self.List(*branches, **kwargs)
def Seq(
self, *sequence, **kwargs)
Seq
is used to express function composition. The expression
Seq(f, g)
be equivalent to
lambda x: g(f(x))
As you see, its a little different from the mathematical definition. Excecution order flow from left to right, this makes reading and reasoning about code way more easy. This bahaviour is based upon the |>
(pipe) operator found in languages like F#, Elixir and Elm. You can pack as many expressions as you like and they will be applied in order to the data that is passed through them when compiled an excecuted.
In general, the following rules apply for Seq:
General Sequence
Seq(f0, f1, ..., fn-1, fn)
is equivalent to
lambda x: fn(fn-1(...(f1(f0(x)))))
Single Function
Seq(f)
is equivalent to
f
Identity
The empty Seq
Seq()
is equivalent to
lambda x: x
Examples
from phi import P, Seq f = Seq( P * 2, P + 1, P ** 2 ) assert f(1) == 9 # ((1 * 2) + 1) ** 2
The previous example using P.Pipe
from phi import P assert 9 == P.Pipe( 1, P * 2, #1 * 2 == 2 P + 1, #2 + 1 == 3 P ** 2 #3 ** 2 == 9 )
def Seq(self, *sequence, **kwargs): """ ` is used to express function composition. The expression Seq(f, g) quivalent to lambda x: g(f(x)) ou see, its a little different from the mathematical definition. Excecution order flow from left to right, this makes reading and reasoning about code way more easy. This bahaviour is based upon the `|>` (pipe) operator found in languages like F#, Elixir and Elm. You can pack as many expressions as you like and they will be applied in order to the data that is passed through them when compiled an excecuted. eneral, the following rules apply for Seq: neral Sequence** Seq(f0, f1, ..., fn-1, fn) quivalent to lambda x: fn(fn-1(...(f1(f0(x))))) ngle Function** Seq(f) quivalent to f entity** empty Seq Seq() quivalent to lambda x: x Examples from phi import P, Seq f = Seq( P * 2, P + 1, P ** 2 ) assert f(1) == 9 # ((1 * 2) + 1) ** 2 previous example using `P.Pipe` from phi import P assert 9 == P.Pipe( 1, P * 2, #1 * 2 == 2 P + 1, #2 + 1 == 3 P ** 2 #3 ** 2 == 9 ) """ fs = [ _parse(elem)._f for elem in sequence ] def g(x, state): return functools.reduce(lambda args, f: f(*args), fs, (x, state)) return self.__then__(g, **kwargs)
def Then(
self, f, *args, **kwargs)
Then(f, ...)
is equivalent to ThenAt(1, f, ...)
. Checkout phi.builder.Builder.ThenAt
for more information.
def Then(self, f, *args, **kwargs): """ n(f, ...)` is equivalent to `ThenAt(1, f, ...)`. Checkout `phi.builder.Builder.ThenAt` for more information. """ return self.ThenAt(1, f, *args, **kwargs)
def Then0(
self, f, *args, **kwargs)
Then0(f, ...)
is equivalent to ThenAt(0, f, ...)
. Checkout phi.builder.Builder.ThenAt
for more information.
def Then0(self, f, *args, **kwargs): """ n0(f, ...)` is equivalent to `ThenAt(0, f, ...)`. Checkout `phi.builder.Builder.ThenAt` for more information. """ return self.ThenAt(0, f, *args, **kwargs)
def Then1(
self, f, *args, **kwargs)
Then(f, ...)
is equivalent to ThenAt(1, f, ...)
. Checkout phi.builder.Builder.ThenAt
for more information.
def Then(self, f, *args, **kwargs): """ n(f, ...)` is equivalent to `ThenAt(1, f, ...)`. Checkout `phi.builder.Builder.ThenAt` for more information. """ return self.ThenAt(1, f, *args, **kwargs)
def Then2(
self, f, arg1, *args, **kwargs)
Then2(f, ...)
is equivalent to ThenAt(2, f, ...)
. Checkout phi.builder.Builder.ThenAt
for more information.
def Then2(self, f, arg1, *args, **kwargs): """ n2(f, ...)` is equivalent to `ThenAt(2, f, ...)`. Checkout `phi.builder.Builder.ThenAt` for more information. """ args = (arg1,) + args return self.ThenAt(2, f, *args, **kwargs)
def Then3(
self, f, arg1, arg2, *args, **kwargs)
Then3(f, ...)
is equivalent to ThenAt(3, f, ...)
. Checkout phi.builder.Builder.ThenAt
for more information.
def Then3(self, f, arg1, arg2, *args, **kwargs): """ n3(f, ...)` is equivalent to `ThenAt(3, f, ...)`. Checkout `phi.builder.Builder.ThenAt` for more information. """ args = (arg1, arg2) + args return self.ThenAt(3, f, *args, **kwargs)
def Then4(
self, f, arg1, arg2, arg3, *args, **kwargs)
Then4(f, ...)
is equivalent to ThenAt(4, f, ...)
. Checkout phi.builder.Builder.ThenAt
for more information.
def Then4(self, f, arg1, arg2, arg3, *args, **kwargs): """ n4(f, ...)` is equivalent to `ThenAt(4, f, ...)`. Checkout `phi.builder.Builder.ThenAt` for more information. """ args = (arg1, arg2, arg3) + args return self.ThenAt(4, f, *args, **kwargs)
def Then5(
self, f, arg1, arg2, arg3, arg4, *args, **kwargs)
Then5(f, ...)
is equivalent to ThenAt(5, f, ...)
. Checkout phi.builder.Builder.ThenAt
for more information.
def Then5(self, f, arg1, arg2, arg3, arg4, *args, **kwargs): """ n5(f, ...)` is equivalent to `ThenAt(5, f, ...)`. Checkout `phi.builder.Builder.ThenAt` for more information. """ args = (arg1, arg2, arg3, arg4) + args return self.ThenAt(5, f, *args, **kwargs)
def ThenAt(
self, n, f, *_args, **kwargs)
ThenAt
enables you to create a partially apply many arguments to a function, the returned partial expects a single arguments which will be applied at the n
th position of the original function.
Arguments
- n: position at which the created partial will apply its awaited argument on the original function.
- f: function which the partial will be created.
- _args & kwargs: all
*_args
and**kwargs
will be passed to the functionf
. _return_type = None
: type of the returnedbuilder
, ifNone
it will return the same type of the currentbuilder
. This special kwarg will NOT be passed tof
.
You can think of n
as the position that the value being piped down will pass through the f
. Say you have the following expression
D == fun(A, B, C)
all the following are equivalent
from phi import P, Pipe, ThenAt D == Pipe(A, ThenAt(1, fun, B, C)) D == Pipe(B, ThenAt(2, fun, A, C)) D == Pipe(C, ThenAt(3, fun, A, B))
you could also use the shortcuts Then
, Then2
,..., Then5
, which are more readable
from phi import P, Pipe D == Pipe(A, P.Then(fun, B, C)) D == Pipe(B, P.Then2(fun, A, C)) D == Pipe(C, P.Then3(fun, A, B))
There is a special case not discussed above: n = 0
. When this happens only the arguments given will be applied to f
, this method it will return a partial that expects a single argument but completely ignores it
from phi import P D == Pipe(None, P.ThenAt(0, fun, A, B, C)) D == Pipe(None, P.Then0(fun, A, B, C))
Examples
Max of 6 and the argument:
from phi import P assert 6 == P.Pipe( 2, P.Then(max, 6) )
Previous is equivalent to
assert 6 == max(2, 6)
Open a file in read mode ('r'
)
from phi import P f = P.Pipe( "file.txt", P.Then(open, 'r') )
Previous is equivalent to
f = open("file.txt", 'r')
Split a string by whitespace and then get the length of each word
from phi import P assert [5, 5, 5] == P.Pipe( "Again hello world", P.Then(str.split, ' ') .Then2(map, len) )
Previous is equivalent to
x = "Again hello world" x = str.split(x, ' ') x = map(len, x) assert [5, 5, 5] == x
As you see, Then2
was very useful because map
accepts and iterable
as its 2nd
parameter. You can rewrite the previous using the PythonBuilder and the phi.builder.Builder.Obj
object
from phi import P, Obj assert [5, 5, 5] == P.Pipe( "Again hello world", Obj.split(' '), P.map(len) )
Also see
phi.builder.Builder.Obj
- PythonBuilder
phi.builder.Builder.RegisterAt
def ThenAt(self, n, f, *_args, **kwargs): """ nAt` enables you to create a partially apply many arguments to a function, the returned partial expects a single arguments which will be applied at the `n`th position of the original function. guments** n**: position at which the created partial will apply its awaited argument on the original function. f**: function which the partial will be created. _args & kwargs**: all `*_args` and `**kwargs` will be passed to the function `f`. return_type = None`: type of the returned `builder`, if `None` it will return the same type of the current `builder`. This special kwarg will NOT be passed to `f`. can think of `n` as the position that the value being piped down will pass through the `f`. Say you have the following expression D == fun(A, B, C) the following are equivalent from phi import P, Pipe, ThenAt D == Pipe(A, ThenAt(1, fun, B, C)) D == Pipe(B, ThenAt(2, fun, A, C)) D == Pipe(C, ThenAt(3, fun, A, B)) could also use the shortcuts `Then`, `Then2`,..., `Then5`, which are more readable from phi import P, Pipe D == Pipe(A, P.Then(fun, B, C)) D == Pipe(B, P.Then2(fun, A, C)) D == Pipe(C, P.Then3(fun, A, B)) e is a special case not discussed above: `n = 0`. When this happens only the arguments given will be applied to `f`, this method it will return a partial that expects a single argument but completely ignores it from phi import P D == Pipe(None, P.ThenAt(0, fun, A, B, C)) D == Pipe(None, P.Then0(fun, A, B, C)) amples** of 6 and the argument: from phi import P assert 6 == P.Pipe( 2, P.Then(max, 6) ) ious is equivalent to assert 6 == max(2, 6) a file in read mode (`'r'`) from phi import P f = P.Pipe( "file.txt", P.Then(open, 'r') ) ious is equivalent to f = open("file.txt", 'r') t a string by whitespace and then get the length of each word from phi import P assert [5, 5, 5] == P.Pipe( "Again hello world", P.Then(str.split, ' ') .Then2(map, len) ) ious is equivalent to x = "Again hello world" x = str.split(x, ' ') x = map(len, x) assert [5, 5, 5] == x ou see, `Then2` was very useful because `map` accepts and `iterable` as its `2nd` parameter. You can rewrite the previous using the [PythonBuilder](https://cgarciae.github.io/phi/python_builder.m.html) and the `phi.builder.Builder.Obj` object from phi import P, Obj assert [5, 5, 5] == P.Pipe( "Again hello world", Obj.split(' '), P.map(len) ) so see** hi.builder.Builder.Obj` ythonBuilder](https://cgarciae.github.io/phi/python_builder.m.html) hi.builder.Builder.RegisterAt` """ _return_type = None n_args = n - 1 if '_return_type' in kwargs: _return_type = kwargs['_return_type'] del kwargs['_return_type'] @utils.lift def g(x): new_args = _args[0:n_args] + (x,) + _args[n_args:] if n_args >= 0 else _args return f(*new_args, **kwargs) return self.__then__(g, _return_type=_return_type)
def Val(
self, val, **kwargs)
The expression
Val(a)
is equivalent to the constant function
lambda x: a
All expression in this module interprete values that are not functions as constant functions using Val
, for example
Seq(1, P + 1)
is equivalent to
Seq(Val(1), P + 1)
The previous expression as a whole is a constant function since it will return 2
no matter what input you give it.
def Val(self, val, **kwargs): """ expression Val(a) quivalent to the constant function lambda x: a expression in this module interprete values that are not functions as constant functions using `Val`, for example Seq(1, P + 1) quivalent to Seq(Val(1), P + 1) previous expression as a whole is a constant function since it will return `2` no matter what input you give it. """ f = utils.lift(lambda z: val) return self.__then__(f, **kwargs)
def With(
self, context_manager, *body, **kwargs)
With
def With(context_manager, *body):
Arguments
- context_manager: a context manager object or valid expression from the DSL that returns a context manager.
- *body: any valid expression of the DSL to be evaluated inside the context.
*body
is interpreted as a tuple so all expression contained are composed.
As with normal python programs you sometimes might want to create a context for a block of code. You normally give a context manager to the with statemente, in Phi you use P.With
or phi.With
Context
Python's with
statemente returns a context object through as
keyword, in the DSL this object can be obtained using the P.Context
method or the phi.Context
function.
Examples
from phi import P, Obj, Context, With, Pipe text = Pipe( "text.txt", With( open, Context, Obj.read() ) )
The previous is equivalent to
with open("text.txt") as f: text = f.read()
def With(self, context_manager, *body, **kwargs): """ th** def With(context_manager, *body): guments** context_manager**: a [context manager](https://docs.python.org/2/reference/datamodel.html#context-managers) object or valid expression from the DSL that returns a context manager. *body**: any valid expression of the DSL to be evaluated inside the context. `*body` is interpreted as a tuple so all expression contained are composed. ith normal python programs you sometimes might want to create a context for a block of code. You normally give a [context manager](https://docs.python.org/2/reference/datamodel.html#context-managers) to the [with](https://docs.python.org/2/reference/compound_stmts.html#the-with-statement) statemente, in Phi you use `P.With` or `phi.With` ntext** on's `with` statemente returns a context object through `as` keyword, in the DSL this object can be obtained using the `P.Context` method or the `phi.Context` function. Examples from phi import P, Obj, Context, With, Pipe text = Pipe( "text.txt", With( open, Context, Obj.read() ) ) previous is equivalent to with open("text.txt") as f: text = f.read() """ context_f = _parse(context_manager)._f body_f = E.Seq(*body)._f def g(x, state): context, state = context_f(x, state) with context as scope: with _WithContextManager(scope): return body_f(x, state) return self.__then__(g, **kwargs)
def Write(
self, *state_args, **state_dict)
See phi.dsl.Expression.Read
def Write(self, *state_args, **state_dict): """See `phi.dsl.Expression.Read`""" if len(state_dict) + len(state_args) < 1: raise Exception("Please include at-least 1 state variable, got {0} and {1}".format(state_args, state_dict)) if len(state_dict) > 1: raise Exception("Please include at-most 1 keyword argument expression, got {0}".format(state_dict)) if len(state_dict) > 0: state_key = next(iter(state_dict.keys())) write_expr = state_dict[state_key] state_args += (state_key,) expr = self >> write_expr else: expr = self def g(x, state): update = { key: x for key in state_args } state = utils.merge(state, update) #side effect for convenience _StateContextManager.REFS.update(state) return x, state return expr.__then__(g)