python generator
This wiki page gives a few hints to get started with the SCL python generator, to understand the design choices/implementation that have been made so far, to introduce the semantic mappings between an EXPRESS schema and the related python module, and at last to present a TODO list. Please report any issue, suggestion, patch, idea etc. For instance:
-
post to the SCL mailing list http://groups.google.com/group/scl-dev
-
open a new issue on the SCL issue tracker http://github.com/stepcode/stepcode/issues. Don't forget to attach the
python generator
orange flag to the new issue. -
you can also email tpaviot@gmail.com.
So far, SCL is known to provide a program named fedex_plus
. fedex_plus parses an EXPRESS schema, and generates C++ code that needs to be compiled in order to provide a C++ API to handle the schema.
The python generator aims at providing the same functionality for the python programming language: parsing any EXPRESS schema, and generating a python module giving access to all entities defined in the schema. This development branch then provides a binary named fedex_python
, as well as a SCL
python package, both enabling access to EXPRESS data represented in a schema (e.g. entities, local or/and global rules, db population etc).
ISO 10303 Part 22 defines a STEP API, named Standard Data Access Interface (SDAI), for C++ (Part 23), C (Part 24) and java (Part 27) programming languages. This gives a strong reference frame for the development of fedex_plus
. Regarding other programming languages (e.g. ruby, scheme, lisp, python etc.), the ISO did not standardize any implementation. The following rules lead to the first public release of the python generator:
- 100% pure python: output of fedex_python should be 100% pure python code, that is to say that only a basic python installation is required to use the generated python module, without any additional dependency to SCL C or C++ code;
- an EXPRESS interpreter: the syntax and semantics of the python code must be as close as possible to the original schema semantics, as well as to the EXPRESS syntax. As a result, the python code should work as a kind of ''EXPRESS interpreter'';
- strongly typed: EXPRESS is strongly typed, whereas python is not. It appears necessary to implemented strong type checking of the arguments and parameters passed to the entity constructors, in order to check the quality of a set of instance or db population.
The python generator was previously in a development stage, and had to be cloned from a separate branch from main. It has been renamed from fedex_python to exp2python.
Related code is available from the src/exp2python directory.
exp2python
-----> CMakeLists.txt: the cmake base file in order to build/install SCL
-----> README.md: README file in the md syntax
-----> examples: some examples of what exp2python should produce and how to deal with that
-----> python: python code of the SCL python package
-----> src: C/C++ code of the exp2python program
By default, the build system will compile both fedex_plus and fedex_python.
First create a directory named cmake-build
at the root StepClassLibrary directory:
$ cd StepClassLibrary $ mkdir cmake-build $ cd cmake-build
Then run cmake:
$ cmake ..
The Makefile is created and available in the ./cmake-build/bin directory. Compile with the usual make command: the exp2python program should be locate into ./cmake-build/bin.
$ make $ cd bin $ ls -l -rwxr-xr-x 1 thomas staff 16136 10 jan 06:02 check-express -rwxr-xr-x 1 thomas staff 252128 10 jan 06:02 fedex_plus -rwxr-xr-x 1 thomas staff 256032 10 jan 06:42 fedex_python
You can check exp2python by just running:
$ ./exp2python -v Build info for ./exp2_python: git commit id v0.8, build timestamp 21:44:08 on Jul 17 2017 http://github.com/stepcode/stepcode and scl-dev on google groups
The generated modules need the SCL package to be installed.
cd to the exp2python/python directory:
$ cd src/exp2python/python
And then run the install script with:
$ python setup.py install
To check that everything was fine, try to import the SCL package from a python prompt:
$ python >>> from SCL.SimpleDataTypes import * >>> dir() ['BINARY', 'BOOLEAN', 'INTEGER', 'LOGICAL', 'NUMBER', 'REAL', 'STRING', 'Unknown', '__builtins__', '__doc__', '__name__', '__package__'] >>>
Everything is now ready for a first test.
The following experiments are conducted with one simple example: the test_simple_geometry
schema, defining three entities (point, vector and circle). Let's consider the file name for this schema is a_simple_schema.exp (if you want to run the following, copy/paste the content below and save it as simple_schema.exp
.
SCHEMA test_simple_geometry; ENTITY point; x : REAL; y : REAL; z : REAL; name : OPTIONAL STRING; END_ENTITY; ENTITY vector; vx : REAL; vy : REAL; name : OPTIONAL STRING; END_ENTITY; ENTITY circle; centre : point; radius : REAL; axis : vector; DERIVE area : REAL := PI*radius**2; perimeter : REAL := 2.0*PI*radius; END_ENTITY; END_SCHEMA;
This schema was chosen because of its simplicity. The purpose is now to create data, from this definition, using the SCL python generator.
This is simply done by running:
$ exp2python simple_schema.exp Running exp2python Resolution successful. Writing python module...Done.
exp2python parsed the express file and generated a python module named test_simple_geometry.py (pattern is the_schema_name.py):
$ ls -l $ ls -l total 24 -rw-r--r--@ 1 thomas staff 366 10 jan 10:01 simple_schema.exp -rw-r--r-- 1 thomas staff 4703 10 jan 10:01 test_simple_geometry.py
We'll see later the contents of this file and the way exp2python mapped EXPRESS to python semantics. But now, let's play with the generated module:
$ python >>> from test_simple_geometry import * >>> dir() ['ABS', 'ACOS', 'ARRAY', 'ASIN', 'ATAN', 'Aggregate', 'Algorithm', 'BAG', 'BINARY', 'BLENGTH', 'BOOLEAN', 'BaseAggregate', 'BaseEntityClass', 'BaseType', 'CONST_E', 'COS', 'ENUMERATION', 'EXISTS', 'EXP', 'EnumerationId', 'FALSE', 'FORMAT', 'FUNCTION', 'HIBOUND', 'HIINDEX', 'INTEGER', 'LENGTH', 'LIST', 'LOBOUND', 'LOG', 'LOG10', 'LOG2', 'LOGICAL', 'LOINDEX', 'NUMBER', 'NVL', 'ODD', 'PI', 'PROCEDURE', 'REAL', 'ROLESOF', 'Rule', 'SCL_float_epsilon', 'SELECT', 'SET', 'SIN', 'SIZEOF', 'STRING', 'TAN', 'TRUE', 'TYPEOF', 'USEDIN', 'Unknown', 'VALUE', 'VALUE_IN', 'VALUE_UNIQUE', '__builtins__', '__doc__', '__name__', '__package__', 'check_type', 'circle', 'math', 'point', 'schema_name', 'schema_scope', 'sys', 'vector'] >>>
In order to create an instance, you must pass all arguments, including optional arguments. point_A is the point of coordinates (1,2.5,3), with name "A":
>>> point_A = point(REAL(1),REAL(2.5),REAL(3),STRING("A"))
Instance attributes can be listed with the usual dir
python commant:
>>> dir(point_A) ['__class__', '__delattr__', '__dict__', '__doc__', '__format__', '__getattribute__', '__hash__', '__init__', '__module__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', '_name', '_x', '_y', '_z', 'name', 'x', 'y', 'z']
The attributes values name, x, y and z are python properties:
>>> point_A.x 1.0 >>> point A.y 2.5 >>> point_A.z 3 >>> point_A.name 'A'
As python properties, they can easily be changed, for instance:
>>> point_A.x = REAL(1.5) >>> point_A.x 1.5
If ever you try to pass the wrong expected type, or if you set to None
the value of an attribute that can't be OPTIONAL, exceptions will be raised:
>>> point_A.x = INTEGER(2) Traceback (most recent call last): File "", line 1, in File "test_simple_geometry.py", line 177, in fset check_type(value,REAL) File "/Library/Python/2.6/site-packages/SCL/TypeChecker.py", line 49, in check_type raise TypeError('Type of argument number_of_sides must be %s (you passed %s)'%(expected_type,type(instance))) TypeError: Type of argument number_of_sides must be (you passed ) >>> point_A.x 1.5 >>> point_A.x = None Traceback (most recent call last): File "", line 1, in File "test_simple_geometry.py", line 176, in fset raise AssertionError('Argument x is mantatory and can not be set to None') AssertionError: Argument x is mantatory and can not be set to None >>> point_A.x 1.5 >>> point_A.name = None >>> point_A
Note that you can inspect attributes values with a simple print
:
>>> print point_A # class description: Entity point definition. :param x :type x:REAL :param y :type y:REAL :param z :type z:REAL :param name :type name:STRING # Instance attributes: name:None x:1.5 y:2.5 z:3.0
>>> vector_v = vector(REAL(1),REAL(0),STRING("V"))
It's now possible to create circle, of radius 3.
- create a circle
>>> c = circle(point_A,REAL(3),vector_v)
area
and perimeter
are known to be DERIVED attributes, that means they can't be set or initialized, but they are inferred.
>>> c.area = REAL(5) Traceback (most recent call last): File "", line 1, in File "test_simple_geometry.py", line 133, in fset raise AssertionError('Argument area is DERIVED. It is computed and can not be set to any value') AssertionError: Argument area is DERIVED. It is computed and can not be set to any value >>> c.area 28.274333882308138 >>> c.perimeter 18.849555921538759
area
and perimeter
were computed according to the mathematical expression defined in the EXPRESS schema. You can change the radius to check that the new values of area and parameter are up-to-date with this change (note that these values are computed when the attribute is called, according to a kind of 'pull' pipe):
>>> c.radius = REAL(4) >>> c.area >>> c.area 50.26548245743669 >>> c.perimeter 25.132741228718345
- entities are mapped to python classes
- OPTIONAL argument
- single and multiple inheritance
- aggregates
- enums and selects
Note: these features still need to be unit tested to check specific use cases that should not been supported.
- use the python super() type to implement multiple inheritance. Python's mro is similar to EXPRESS
- move the current implementation of expression evaluation from eval() (which is known to be unsafe) to python functions
- Part21 importer: a draft for a Part21 reader is available in the SCL package. It has not been tested for a while and has to be modified.
- enums take strings parameters. They should rather take instances.
- run exp2python on STEP IS schema files (ap203, ap214 etc.)
- extend the set of supported unitary schemas
- Part28 importer
- Part21/28 exporter
- local rules checking
- port to Python 3
- [Please add your suggestions here]