diff --git a/docs/build/doctrees/api.doctree b/docs/build/doctrees/api.doctree
index 9a15a5c..ac84847 100644
Binary files a/docs/build/doctrees/api.doctree and b/docs/build/doctrees/api.doctree differ
diff --git a/docs/build/doctrees/environment.pickle b/docs/build/doctrees/environment.pickle
index dd0b2a0..20ea892 100644
Binary files a/docs/build/doctrees/environment.pickle and b/docs/build/doctrees/environment.pickle differ
diff --git a/docs/build/doctrees/index.doctree b/docs/build/doctrees/index.doctree
index 0d1d704..0e317f3 100644
Binary files a/docs/build/doctrees/index.doctree and b/docs/build/doctrees/index.doctree differ
diff --git a/docs/build/doctrees/integration.doctree b/docs/build/doctrees/integration.doctree
index de7c8e2..93116f9 100644
Binary files a/docs/build/doctrees/integration.doctree and b/docs/build/doctrees/integration.doctree differ
diff --git a/docs/build/doctrees/structure.doctree b/docs/build/doctrees/structure.doctree
index ee659a0..1ed155a 100644
Binary files a/docs/build/doctrees/structure.doctree and b/docs/build/doctrees/structure.doctree differ
diff --git a/docs/build/html/_images/plantuml-36fad8472301586150ff18c26187340f47bb62fa.png b/docs/build/html/_images/plantuml-36fad8472301586150ff18c26187340f47bb62fa.png
new file mode 100644
index 0000000..1d2ec51
Binary files /dev/null and b/docs/build/html/_images/plantuml-36fad8472301586150ff18c26187340f47bb62fa.png differ
diff --git a/docs/build/html/_modules/celpy/__init__.html b/docs/build/html/_modules/celpy/__init__.html
index 75f87b7..6a41db2 100644
--- a/docs/build/html/_modules/celpy/__init__.html
+++ b/docs/build/html/_modules/celpy/__init__.html
@@ -114,20 +114,24 @@
Source code for celpy.__init__
classRunner(abc.ABC):"""Abstract runner for a compiled CEL program.
- The :py:class:`Environment` creates a :py:class:`Runner` to permit
+ The :py:class:`Environment` creates :py:class:`Runner` objects to permit saving a ready-tp-evaluate, compiled CEL expression. A :py:class:`Runner` will evaluate the AST in the context of a specific activation with the provided variable values.
- A :py:class:`Runner` class provides
- the ``tree_node_class`` attribute to define the type for tree nodes.
- This class information is used by the :py:class:`Environment` to tailor the ``Lark`` instance created.
- The class named by the ``tree_node_class`` can include specialized AST features
- needed by a :py:class:`Runner` instance.
+ The py:meth:`Runner.evaluate` method is used to evaluate a CEL expression with a new data context.
+
+ As an implementation detail, note that
+ each :py:class:`Runner` subclass definition includes
+ the ``tree_node_class`` attribute.
+ This attribute defines the type for Tree nodes that must be created by the :py:mod:`lark` parser.
+ This class information provided to the :py:class:`Environment` to tailor the :py:mod:`lark` parser.
+ The class named often includes specialized AST features
+ needed by the :py:class:`Runner` subclss. .. todo:: For a better fit with Go language expectations
- Consider addoing type adapter and type provider registries.
+ Consider adding type adapter and type provider registries. This would permit distinct sources of protobuf message types. """
@@ -164,6 +168,8 @@
Source code for celpy.__init__
""" Builds a new, working :py:class:`Activation` using the :py:class:`Environment` as defaults. A Context will later be layered onto this for evaluation.
+
+ This is used internally during evaluation. """base_activation=Activation(package=self.environment.package,
@@ -180,9 +186,13 @@
Source code for celpy.__init__
""" Given variable definitions in the :py:class:`celpy.evaluation.Context`, evaluate the given AST and return the resulting value.
- Generally, this should raise an :exc:`CELEvalError` for most kinds of ordinary problems.
- It may raise an :exc:`CELUnsupportedError` for future features that aren't fully implemented.
+ Generally, this should raise an :exc:`celpy.evaluation.CELEvalError` for most kinds of ordinary problems.
+ It may raise an :exc:`celpy.evaluation.CELUnsupportedError` for future features that aren't fully implemented. Any Python exception reflects a serious problem.
+
+ :param activation: a :py:class:`celpy.evaluation.Context` object with variable values to use for this evaluation.
+ :returns: the computed value
+ :raises: :exc:`celpy.evaluation.CELEvalError` or :exc:`celpy.evaluation.CELUnsupportedError` for problems encounterd. """...
@@ -305,11 +315,30 @@
Source code for celpy.__init__
classEnvironment:""" Contains the current evaluation context.
- The :py:meth:`Environment.compile` method
+
+ CEL integration starts by creating an :py:class:`Environment` object.
+ This can be initialized with three optional values:
+
+ - A package name used to resolve variable names.
+ This is not generally required, but is sometimes helpful to provide an explicit namespace for variables.
+
+ - Type annotations for variables.
+ This helps perform type conversions on external data.
+
+ - The class of runner to use. By default an :py:class:`InterpretedRunner` is used.
+ The alternative is the :py:class:`CompiledRunner`.
+ Detailed performance benchmarks are still pending.
+ Detailed logging is available from the interpreted runner, to help debug external function bindings.
+
+ Once the environment has been created, the :py:meth:`Environment.compile` method compiles CEL text to create an AST.
+ This can be helpful for an application that needs to prepare error messages based on the AST.
+ An application can also optimize or transform the AST. The :py:meth:`Environment.program` method
- packages the AST into a program ready for evaluation.
+ packages the AST into a :py:class:`Runnable` ready for evaluation.
+ At this time, external functions are bound to the CEL expression.
+ The :py:class:`Runnable` can be evaluated repeatedly with multiple inputs, avoiding the overheads of compiling for each input value. .. todo:: For a better fit with Go language expectations
@@ -364,7 +393,20 @@
Source code for celpy.__init__
[docs]defcompile(self,text:str)->Expression:
-"""Compile the CEL source. This can raise syntax error exceptions."""
+"""
+ Compiles the CEL source.
+
+ Processing starts here by building an AST structure from the CEL text.
+ The AST is exposed for the rare case where an application needs to transform it or analyze it.
+ Generally, it's best to treat the AST object as opaque, and provide it to the :py:meth:`program` method.
+
+ This can raise syntax error exceptions.
+ The exceptions contain line and character position information to help create easy-to-use error outputs.
+
+ :param text: The CEL text to evaluate.
+ :returns: A :py:class:`lark.Tree` object describing the CEL expression.
+ :raises: :py:class:`celpy.celparser.CELParseError` exceptions for syntax errors.
+ """ast=self.cel_parser.parse(text)returnast
@@ -376,6 +418,9 @@
Source code for celpy.__init__
)->Runner:""" Transforms the AST into an executable :py:class:`Runner` object.
+ This will bind the given functions into the runnable object.
+
+ The resulting object has a :py:meth:`Runner.evaluate` method that applies the CEL structure to input data to compute the final result. :param expr: The parse tree from :py:meth:`compile`. :param functions: Any additional functions to be used by this CEL expression.
@@ -422,7 +467,7 @@
# See the License for the specific language governing permissions and limitations under the License."""
-Converts some Python-native types into CEL structures.
+Adapters to convert some Python-native types into CEL structures.Currently, atomic Python objects have direct use of types in :mod:`celpy.celtypes`.
@@ -155,9 +155,30 @@
Source code for celpy.adapter
[docs]defjson_to_cel(document:JSON)->celtypes.Value:
-"""Convert parsed JSON object from Python to CEL to the extent possible.
+"""
+ Converts parsed JSON object from Python to CEL to the extent possible.
+
+ Note that it's difficult to distinguish strings which should be timestamps or durations.
+ Using the :py:mod:`json` package ``objecthook`` can help do these conversions.
+
+ .. csv-table::
+ :header: python, CEL
+
+ bool, :py:class:`celpy.celtypes.BoolType`
+ float, :py:class:`celpy.celtypes.DoubleType`
+ int, :py:class:`celpy.celtypes.IntType`
+ str, :py:class:`celpy.celtypes.StringType`
+ None, None
+ "tuple, list", :py:class:`celpy.celtypes.ListType`
+ dict, :py:class:`celpy.celtypes.MapType`
+ datetime.datetime, :py:class:`celpy.celtypes.TimestampType`
+ datetime.timedelta, :py:class:`celpy.celtypes.DurationType`
+
+ :param document: A JSON document.
+ :returns: :py:class:`celpy.celtypes.Value`.
+ :raises: internal :exc:`ValueError` or :exc:`TypeError` for failed conversions.
- It's difficult to distinguish strings which should be timestamps or durations.
+ Example: ::
@@ -227,8 +248,9 @@
importcelpy.celtypesfromcelpy.celparserimporttree_dump
-_USE_RE2=False# Used by the test suite.try:importre2
- _USE_RE2=True# Used by the test suite.
-
deffunction_matches(text:str,pattern:str)->"Result":
+"""Implementation of the ``match()`` function using ``re2``"""try:m=re2.search(pattern,text)exceptre2.errorasex:
@@ -132,10 +130,14 @@
Source code for celpy.evaluation
returncelpy.celtypes.BoolType(misnotNone)exceptImportError:# pragma: no cover
+ # There is a build issue with python_version=='3.13' and sys_platform=='darwin'
+ # See https://github.com/google/re2/issues/516
+ # We fall back to using re, which passes the essential tests
[docs]deffunction_matches(text:str,pattern:str)->"Result":
+"""Alternative implementation of the ``match()`` function for systems where ``re2`` can't be installed."""try:m=re.search(pattern,text)exceptre.errorasex:
@@ -4391,7 +4393,7 @@
diff --git a/docs/build/html/_plantuml/36/36fad8472301586150ff18c26187340f47bb62fa.png b/docs/build/html/_plantuml/36/36fad8472301586150ff18c26187340f47bb62fa.png
new file mode 100644
index 0000000..1d2ec51
Binary files /dev/null and b/docs/build/html/_plantuml/36/36fad8472301586150ff18c26187340f47bb62fa.png differ
diff --git a/docs/build/html/_sources/api.rst.txt b/docs/build/html/_sources/api.rst.txt
index 7515b4f..1ccd445 100644
--- a/docs/build/html/_sources/api.rst.txt
+++ b/docs/build/html/_sources/api.rst.txt
@@ -2,13 +2,13 @@
# Copyright 2020 The Cloud Custodian Authors.
# SPDX-License-Identifier: Apache-2.0
-.. _`api`:
+.. _`api.reference`:
-###########
-API
-###########
+#############
+API Reference
+#############
-Details of the CEL-Python implementation and the API to the various components.
+Detailed reference material for the CEL-Python implementation.
The following modules are described here:
diff --git a/docs/build/html/_sources/index.rst.txt b/docs/build/html/_sources/index.rst.txt
index 5e3ab46..b1a949d 100644
--- a/docs/build/html/_sources/index.rst.txt
+++ b/docs/build/html/_sources/index.rst.txt
@@ -44,7 +44,7 @@ Integration Overview
Interested in the API for using this package? There are three key topics:
- :ref:`integration`
-- :ref:`api`
+- :ref:`api.reference`
- :ref:`data_structures`
The integration into another application is often a bit more than an ``import``.
diff --git a/docs/build/html/_sources/integration.rst.txt b/docs/build/html/_sources/integration.rst.txt
index c36bfaa..51feba2 100644
--- a/docs/build/html/_sources/integration.rst.txt
+++ b/docs/build/html/_sources/integration.rst.txt
@@ -10,17 +10,19 @@ Application Integration
We'll look at integration of CEL into another application from four perspectives:
-1. We'll look at the essential base case for integration into another application.
- This will use an ``Activation`` to provide values for variables.
+1. We'll start with `Integration Essentials`_. This is the base case for integration into another application.
-2. A more sophisticated integration involves extending the environment with custom functions.
- This can provide a well-defined interface between CEL expressions and application functionality.
+2. In `Function Bindings`_, we'll look at a more sophisticated integration. This extends the environment with custom functions.
+ This can provide a well-defined interface between CEL expressions and your application's functionality.
-3. Some additional examples from the Go implementation show how extend the environment using new types.
+3. `More Examples from the Go implementation`_ shows how extend the environment using new types.
+ Python's use of duck typing removes some of the complexity of the Go implementation.
-4. There are a few exception and error-handling cases that are part of working with Python types.
+4. There are a few exception and error-handling cases covered in `Exceptions and Errors`_.
-5. Finally, we'll look at how CEL can be integrated into Cloud Custodian (C7N).
+5. The `Cloud Custodian (C7N) Integration`_ is rather complicated because the C7N is covers quite a large number of distinct data types.
+
+5. Finally, `External API`_ will review some elements of the API that are part of the integration interface.
Integration Essentials
======================
@@ -309,8 +311,8 @@ The ``shake_hands()`` function is essentially the same as the ``greet()`` functi
For more examples of how to use CEL from Go, see
https://github.com/google/cel-go/tree/master/cel/cel_test.go
-More Examples from Go implementation
-=====================================
+More Examples from the Go implementation
+=========================================
See https://github.com/google/cel-go/blob/master/README.md for five more examples.
@@ -584,3 +586,54 @@ The breakdown of ``filter`` rules in the C7N policy schema has the following cou
"('Singleton', 'No-Op')",47,"Used for exactly one resource type, does not expose resource details"
(This is based on cloud-custodian-0.8.40.0, newer versions may have slighyly different numbers.)
+
+External API
+=============
+
+The key external components are the following:
+
+- :py:class:`celpy.__init__.Environment`
+
+ This has two methods of interest:
+
+ - :py:meth:`celpy.__init__.Environment.compile`
+
+ - :py:meth:`celpy.__init__.Environment.program`
+
+- :py:class:`celpy.__init__.Runner`
+
+ This has one method of interest:
+
+ - :py:meth:`celpy.__init__.Runner.evaluate`.
+
+- :py:func:`celpy.adapter.json_to_cel`
+
+ This is used to convert native Python JSON documents to the appropriate CEL types.
+
+.. uml::
+
+ @startuml
+ class YourApp
+
+ package celpy {
+ class Environment {
+ compile()
+ program()
+ }
+ abstract class Runner {
+ evaluate(context)
+ }
+ Environment -> Runner
+
+ class CompiledRunner
+ class InterpretedRunner
+
+ Runner <|-- CompiledRunner
+ Runner <|-- InterpretedRunner
+ }
+
+ YourApp *--> Environment : "Creates"
+
+ YourApp *--> Runner : "Evaluates"
+
+ @enduml
diff --git a/docs/build/html/_sources/structure.rst.txt b/docs/build/html/_sources/structure.rst.txt
index 5517636..109309e 100644
--- a/docs/build/html/_sources/structure.rst.txt
+++ b/docs/build/html/_sources/structure.rst.txt
@@ -28,7 +28,7 @@ We'll start with the C4 views:
- `The member-dot Production`_
-The code view is in the :ref:`api` section.
+The code view is in the :ref:`api.reference` section.
Context
=======
diff --git a/docs/build/html/api.html b/docs/build/html/api.html
index 5ede583..23893af 100644
--- a/docs/build/html/api.html
+++ b/docs/build/html/api.html
@@ -5,7 +5,7 @@
- API — CEL in Python documentation
+ API Reference — CEL in Python documentation
@@ -33,9 +33,9 @@
The Environment creates Runner objects to permit
saving a ready-tp-evaluate, compiled CEL expression.
A Runner will evaluate the AST in the context of a specific activation
with the provided variable values.
-
A Runner class provides
-the tree_node_class attribute to define the type for tree nodes.
-This class information is used by the Environment to tailor the Lark instance created.
-The class named by the tree_node_class can include specialized AST features
-needed by a Runner instance.
+
The py:meth:Runner.evaluate method is used to evaluate a CEL expression with a new data context.
+
As an implementation detail, note that
+each Runner subclass definition includes
+the tree_node_class attribute.
+This attribute defines the type for Tree nodes that must be created by the lark parser.
+This class information provided to the Environment to tailor the lark parser.
+The class named often includes specialized AST features
+needed by the Runner subclss.
Given variable definitions in the celpy.evaluation.Context, evaluate the given AST and return the resulting value.
-
Generally, this should raise an CELEvalError for most kinds of ordinary problems.
-It may raise an CELUnsupportedError for future features that aren’t fully implemented.
+
Given variable definitions in the celpy.evaluation.Context, evaluate the given AST and return the resulting value.
-
Generally, this should raise an CELEvalError for most kinds of ordinary problems.
-It may raise an CELUnsupportedError for future features that aren’t fully implemented.
+
Contains the current evaluation context.
-The Environment.compile() method
-compiles CEL text to create an AST.
+
Contains the current evaluation context.
+
CEL integration starts by creating an Environment object.
+This can be initialized with three optional values:
+
+
A package name used to resolve variable names.
+This is not generally required, but is sometimes helpful to provide an explicit namespace for variables.
+
Type annotations for variables.
+This helps perform type conversions on external data.
+
The class of runner to use. By default an InterpretedRunner is used.
+The alternative is the CompiledRunner.
+Detailed performance benchmarks are still pending.
+Detailed logging is available from the interpreted runner, to help debug external function bindings.
+
+
Once the environment has been created, the Environment.compile() method
+compiles CEL text to create an AST.
+This can be helpful for an application that needs to prepare error messages based on the AST.
+An application can also optimize or transform the AST.
The Environment.program() method
-packages the AST into a program ready for evaluation.
+packages the AST into a Runnable ready for evaluation.
+At this time, external functions are bound to the CEL expression.
+The Runnable can be evaluated repeatedly with multiple inputs, avoiding the overheads of compiling for each input value.
Compile the CEL source. This can raise syntax error exceptions.
+
Compiles the CEL source.
+
Processing starts here by building an AST structure from the CEL text.
+The AST is exposed for the rare case where an application needs to transform it or analyze it.
+Generally, it’s best to treat the AST object as opaque, and provide it to the program() method.
+
This can raise syntax error exceptions.
+The exceptions contain line and character position information to help create easy-to-use error outputs.
Convert parsed JSON object from Python to CEL to the extent possible.
-
It’s difficult to distinguish strings which should be timestamps or durations.
+
Converts parsed JSON object from Python to CEL to the extent possible.
+
Note that it’s difficult to distinguish strings which should be timestamps or durations.
+Using the json package objecthook can help do these conversions.
Given variable definitions in the celpy.evaluation.Context, evaluate the given AST and return the resulting value.
-
Generally, this should raise an CELEvalError for most kinds of ordinary problems.
-It may raise an CELUnsupportedError for future features that aren’t fully implemented.
+
C7N Extension functions can rely on a global C7N object. This is the celpy.c7nlib.C7NContext instance
+
C7N Extension functions can rely on a global C7N object. This is the celpy.c7nlib.C7NContext instance
used to manage C7N CEL Execution. It generally has one attribute, filter which is the
current CELFilter instance.
Getting metric statistics for a specific resource.
-The celpy.c7nlib.get_metrics() function takes parameters for period, start, end,
+The celpy.c7nlib.get_metrics() function takes parameters for period, start, end,
and the statistics value to compute. The dimension comes from the Resource.
-This uses celpy.c7nlib.get_raw_metrics().
Generally, C7N requests in bunches of 50 per client connection.
A worker pool processes the batches to keep from overwhelming AWS with
@@ -2128,7 +2128,7 @@
There are two examples of type:credential filters. These look at the credentials associated with IAM roles.
-See the celpy.c7nlib.credentials() function to fetch the related resource.
Note the complex regex: (?!WIN.*). This does not translated trivially to CEL: a manual
revision to this filter is strongly suggested, something like this:
Getting health events for a specific resource.
-The celpy.c7nlib.get_health_events() function builds a filter unique to the resource.
-This uses celpy.c7nlib.get_raw_health_events().
Generally, C7N requests in bunches of 10 per client connection.
A worker pool processes the batches to keep from overwhelming AWS with
@@ -9962,8 +9962,9 @@
We’ll look at integration of CEL into another application from four perspectives:
-
We’ll look at the essential base case for integration into another application.
-This will use an Activation to provide values for variables.
-
A more sophisticated integration involves extending the environment with custom functions.
-This can provide a well-defined interface between CEL expressions and application functionality.
-
Some additional examples from the Go implementation show how extend the environment using new types.
-
There are a few exception and error-handling cases that are part of working with Python types.
-
Finally, we’ll look at how CEL can be integrated into Cloud Custodian (C7N).
+
We’ll start with Integration Essentials. This is the base case for integration into another application.
+
In Function Bindings, we’ll look at a more sophisticated integration. This extends the environment with custom functions.
+This can provide a well-defined interface between CEL expressions and your application’s functionality.
+
More Examples from the Go implementation shows how extend the environment using new types.
+Python’s use of duck typing removes some of the complexity of the Go implementation.
+
There are a few exception and error-handling cases covered in Exceptions and Errors.
diff --git a/docs/source/api.rst b/docs/source/api.rst
index 7515b4f..1ccd445 100644
--- a/docs/source/api.rst
+++ b/docs/source/api.rst
@@ -2,13 +2,13 @@
# Copyright 2020 The Cloud Custodian Authors.
# SPDX-License-Identifier: Apache-2.0
-.. _`api`:
+.. _`api.reference`:
-###########
-API
-###########
+#############
+API Reference
+#############
-Details of the CEL-Python implementation and the API to the various components.
+Detailed reference material for the CEL-Python implementation.
The following modules are described here:
diff --git a/docs/source/index.rst b/docs/source/index.rst
index 5e3ab46..b1a949d 100644
--- a/docs/source/index.rst
+++ b/docs/source/index.rst
@@ -44,7 +44,7 @@ Integration Overview
Interested in the API for using this package? There are three key topics:
- :ref:`integration`
-- :ref:`api`
+- :ref:`api.reference`
- :ref:`data_structures`
The integration into another application is often a bit more than an ``import``.
diff --git a/docs/source/integration.rst b/docs/source/integration.rst
index c36bfaa..51feba2 100644
--- a/docs/source/integration.rst
+++ b/docs/source/integration.rst
@@ -10,17 +10,19 @@ Application Integration
We'll look at integration of CEL into another application from four perspectives:
-1. We'll look at the essential base case for integration into another application.
- This will use an ``Activation`` to provide values for variables.
+1. We'll start with `Integration Essentials`_. This is the base case for integration into another application.
-2. A more sophisticated integration involves extending the environment with custom functions.
- This can provide a well-defined interface between CEL expressions and application functionality.
+2. In `Function Bindings`_, we'll look at a more sophisticated integration. This extends the environment with custom functions.
+ This can provide a well-defined interface between CEL expressions and your application's functionality.
-3. Some additional examples from the Go implementation show how extend the environment using new types.
+3. `More Examples from the Go implementation`_ shows how extend the environment using new types.
+ Python's use of duck typing removes some of the complexity of the Go implementation.
-4. There are a few exception and error-handling cases that are part of working with Python types.
+4. There are a few exception and error-handling cases covered in `Exceptions and Errors`_.
-5. Finally, we'll look at how CEL can be integrated into Cloud Custodian (C7N).
+5. The `Cloud Custodian (C7N) Integration`_ is rather complicated because the C7N is covers quite a large number of distinct data types.
+
+5. Finally, `External API`_ will review some elements of the API that are part of the integration interface.
Integration Essentials
======================
@@ -309,8 +311,8 @@ The ``shake_hands()`` function is essentially the same as the ``greet()`` functi
For more examples of how to use CEL from Go, see
https://github.com/google/cel-go/tree/master/cel/cel_test.go
-More Examples from Go implementation
-=====================================
+More Examples from the Go implementation
+=========================================
See https://github.com/google/cel-go/blob/master/README.md for five more examples.
@@ -584,3 +586,54 @@ The breakdown of ``filter`` rules in the C7N policy schema has the following cou
"('Singleton', 'No-Op')",47,"Used for exactly one resource type, does not expose resource details"
(This is based on cloud-custodian-0.8.40.0, newer versions may have slighyly different numbers.)
+
+External API
+=============
+
+The key external components are the following:
+
+- :py:class:`celpy.__init__.Environment`
+
+ This has two methods of interest:
+
+ - :py:meth:`celpy.__init__.Environment.compile`
+
+ - :py:meth:`celpy.__init__.Environment.program`
+
+- :py:class:`celpy.__init__.Runner`
+
+ This has one method of interest:
+
+ - :py:meth:`celpy.__init__.Runner.evaluate`.
+
+- :py:func:`celpy.adapter.json_to_cel`
+
+ This is used to convert native Python JSON documents to the appropriate CEL types.
+
+.. uml::
+
+ @startuml
+ class YourApp
+
+ package celpy {
+ class Environment {
+ compile()
+ program()
+ }
+ abstract class Runner {
+ evaluate(context)
+ }
+ Environment -> Runner
+
+ class CompiledRunner
+ class InterpretedRunner
+
+ Runner <|-- CompiledRunner
+ Runner <|-- InterpretedRunner
+ }
+
+ YourApp *--> Environment : "Creates"
+
+ YourApp *--> Runner : "Evaluates"
+
+ @enduml
diff --git a/docs/source/structure.rst b/docs/source/structure.rst
index 5517636..109309e 100644
--- a/docs/source/structure.rst
+++ b/docs/source/structure.rst
@@ -28,7 +28,7 @@ We'll start with the C4 views:
- `The member-dot Production`_
-The code view is in the :ref:`api` section.
+The code view is in the :ref:`api.reference` section.
Context
=======
diff --git a/src/celpy/__init__.py b/src/celpy/__init__.py
index 5b8e592..574bcf0 100644
--- a/src/celpy/__init__.py
+++ b/src/celpy/__init__.py
@@ -79,20 +79,24 @@
class Runner(abc.ABC):
"""Abstract runner for a compiled CEL program.
- The :py:class:`Environment` creates a :py:class:`Runner` to permit
+ The :py:class:`Environment` creates :py:class:`Runner` objects to permit
saving a ready-tp-evaluate, compiled CEL expression.
A :py:class:`Runner` will evaluate the AST in the context of a specific activation
with the provided variable values.
- A :py:class:`Runner` class provides
- the ``tree_node_class`` attribute to define the type for tree nodes.
- This class information is used by the :py:class:`Environment` to tailor the ``Lark`` instance created.
- The class named by the ``tree_node_class`` can include specialized AST features
- needed by a :py:class:`Runner` instance.
+ The py:meth:`Runner.evaluate` method is used to evaluate a CEL expression with a new data context.
+
+ As an implementation detail, note that
+ each :py:class:`Runner` subclass definition includes
+ the ``tree_node_class`` attribute.
+ This attribute defines the type for Tree nodes that must be created by the :py:mod:`lark` parser.
+ This class information provided to the :py:class:`Environment` to tailor the :py:mod:`lark` parser.
+ The class named often includes specialized AST features
+ needed by the :py:class:`Runner` subclss.
.. todo:: For a better fit with Go language expectations
- Consider addoing type adapter and type provider registries.
+ Consider adding type adapter and type provider registries.
This would permit distinct sources of protobuf message types.
"""
@@ -121,6 +125,8 @@ def new_activation(self) -> Activation:
"""
Builds a new, working :py:class:`Activation` using the :py:class:`Environment` as defaults.
A Context will later be layered onto this for evaluation.
+
+ This is used internally during evaluation.
"""
base_activation = Activation(
package=self.environment.package,
@@ -134,9 +140,13 @@ def evaluate(self, activation: Context) -> celpy.celtypes.Value: # pragma: no c
"""
Given variable definitions in the :py:class:`celpy.evaluation.Context`, evaluate the given AST and return the resulting value.
- Generally, this should raise an :exc:`CELEvalError` for most kinds of ordinary problems.
- It may raise an :exc:`CELUnsupportedError` for future features that aren't fully implemented.
+ Generally, this should raise an :exc:`celpy.evaluation.CELEvalError` for most kinds of ordinary problems.
+ It may raise an :exc:`celpy.evaluation.CELUnsupportedError` for future features that aren't fully implemented.
Any Python exception reflects a serious problem.
+
+ :param activation: a :py:class:`celpy.evaluation.Context` object with variable values to use for this evaluation.
+ :returns: the computed value
+ :raises: :exc:`celpy.evaluation.CELEvalError` or :exc:`celpy.evaluation.CELUnsupportedError` for problems encounterd.
"""
...
@@ -234,11 +244,30 @@ def __new__(
class Environment:
"""
Contains the current evaluation context.
- The :py:meth:`Environment.compile` method
+
+ CEL integration starts by creating an :py:class:`Environment` object.
+ This can be initialized with three optional values:
+
+ - A package name used to resolve variable names.
+ This is not generally required, but is sometimes helpful to provide an explicit namespace for variables.
+
+ - Type annotations for variables.
+ This helps perform type conversions on external data.
+
+ - The class of runner to use. By default an :py:class:`InterpretedRunner` is used.
+ The alternative is the :py:class:`CompiledRunner`.
+ Detailed performance benchmarks are still pending.
+ Detailed logging is available from the interpreted runner, to help debug external function bindings.
+
+ Once the environment has been created, the :py:meth:`Environment.compile` method
compiles CEL text to create an AST.
+ This can be helpful for an application that needs to prepare error messages based on the AST.
+ An application can also optimize or transform the AST.
The :py:meth:`Environment.program` method
- packages the AST into a program ready for evaluation.
+ packages the AST into a :py:class:`Runnable` ready for evaluation.
+ At this time, external functions are bound to the CEL expression.
+ The :py:class:`Runnable` can be evaluated repeatedly with multiple inputs, avoiding the overheads of compiling for each input value.
.. todo:: For a better fit with Go language expectations
@@ -285,7 +314,20 @@ def __repr__(self) -> str:
return f"{self.__class__.__name__}({self.package}, {self.annotations}, {self.runner_class})"
def compile(self, text: str) -> Expression:
- """Compile the CEL source. This can raise syntax error exceptions."""
+ """
+ Compiles the CEL source.
+
+ Processing starts here by building an AST structure from the CEL text.
+ The AST is exposed for the rare case where an application needs to transform it or analyze it.
+ Generally, it's best to treat the AST object as opaque, and provide it to the :py:meth:`program` method.
+
+ This can raise syntax error exceptions.
+ The exceptions contain line and character position information to help create easy-to-use error outputs.
+
+ :param text: The CEL text to evaluate.
+ :returns: A :py:class:`lark.Tree` object describing the CEL expression.
+ :raises: :py:class:`celpy.celparser.CELParseError` exceptions for syntax errors.
+ """
ast = self.cel_parser.parse(text)
return ast
@@ -294,6 +336,9 @@ def program(
) -> Runner:
"""
Transforms the AST into an executable :py:class:`Runner` object.
+ This will bind the given functions into the runnable object.
+
+ The resulting object has a :py:meth:`Runner.evaluate` method that applies the CEL structure to input data to compute the final result.
:param expr: The parse tree from :py:meth:`compile`.
:param functions: Any additional functions to be used by this CEL expression.
diff --git a/src/celpy/adapter.py b/src/celpy/adapter.py
index 154af76..8f7d6d2 100644
--- a/src/celpy/adapter.py
+++ b/src/celpy/adapter.py
@@ -14,7 +14,7 @@
# See the License for the specific language governing permissions and limitations under the License.
"""
-Converts some Python-native types into CEL structures.
+Adapters to convert some Python-native types into CEL structures.
Currently, atomic Python objects have direct use of types in :mod:`celpy.celtypes`.
@@ -102,9 +102,30 @@ def decode(self, source: str, _w: Any = None) -> Any:
def json_to_cel(document: JSON) -> celtypes.Value:
- """Convert parsed JSON object from Python to CEL to the extent possible.
+ """
+ Converts parsed JSON object from Python to CEL to the extent possible.
+
+ Note that it's difficult to distinguish strings which should be timestamps or durations.
+ Using the :py:mod:`json` package ``objecthook`` can help do these conversions.
+
+ .. csv-table::
+ :header: python, CEL
+
+ bool, :py:class:`celpy.celtypes.BoolType`
+ float, :py:class:`celpy.celtypes.DoubleType`
+ int, :py:class:`celpy.celtypes.IntType`
+ str, :py:class:`celpy.celtypes.StringType`
+ None, None
+ "tuple, list", :py:class:`celpy.celtypes.ListType`
+ dict, :py:class:`celpy.celtypes.MapType`
+ datetime.datetime, :py:class:`celpy.celtypes.TimestampType`
+ datetime.timedelta, :py:class:`celpy.celtypes.DurationType`
+
+ :param document: A JSON document.
+ :returns: :py:class:`celpy.celtypes.Value`.
+ :raises: internal :exc:`ValueError` or :exc:`TypeError` for failed conversions.
- It's difficult to distinguish strings which should be timestamps or durations.
+ Example:
::