Join GitHub today
GitHub is home to over 28 million developers working together to host and review code, manage projects, and build software together.Sign up
Clone this wiki locally
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 generatororange flag to the new issue.
you can also email email@example.com.
1. Purpose of the python generator
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).
2. Design Guidelines
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.
3. How to get it
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.
-----> 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
4. How to build
4.1. Build exp2python
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
4.2. Install the SCL python package
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.
5. Getting started
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
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.
5.1. Process exp2python over the EXPRESS schema
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'] >>>
5.2. Let's create a point instance
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 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
5.3 Create a vector
>>> vector_v = vector(REAL(1),REAL(0),STRING("V"))
5.4 Create a circle
It's now possible to create circle, of radius 3.
- create a circle
>>> c = circle(point_A,REAL(3),vector_v)
5.5 Playing with DERIVED attributes
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
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
6. Implemented features
- entities are mapped to python classes
- OPTIONAL argument
- single and multiple inheritance
- enums and selects
Note: these features still need to be unit tested to check specific use cases that should not been supported.
7. TODO LIST
7.1. On going work
- 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]