-
Notifications
You must be signed in to change notification settings - Fork 30
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #384 from ielis/python/include-generated-files
Include the generated files into Python library
- Loading branch information
Showing
27 changed files
with
640 additions
and
200 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,45 @@ | ||
# This workflow will generate a Python protobuf bindings and type stubs with Maven and run Python tests. | ||
|
||
name: Python CI with Maven and Pytest | ||
|
||
on: | ||
push: | ||
branches: [ "master" ] | ||
pull_request: | ||
branches: [ "master" ] | ||
workflow_dispatch: | ||
|
||
jobs: | ||
run-python-ci: | ||
runs-on: ubuntu-latest | ||
strategy: | ||
matrix: | ||
python-version: ['3.8', '3.9', '3.10', '3.11', '3.12'] | ||
|
||
steps: | ||
- uses: actions/checkout@v4 | ||
with: | ||
submodules: recursive | ||
|
||
- name: Set up JDK 17 | ||
uses: actions/setup-java@v4 | ||
with: | ||
java-version: '17' | ||
distribution: 'temurin' | ||
|
||
- name: Set up Python | ||
uses: actions/setup-python@v4 | ||
with: | ||
python-version: ${{ matrix.python-version }} | ||
|
||
- name: Generate Python bindings with Maven | ||
run: ./mvnw -B package -DskipTests # we run tests elsewhere | ||
|
||
- name: Install Python bindings | ||
run: | | ||
cd python && python3 -m pip install .[test] | ||
- name: Run Python tests | ||
run: | | ||
cd python && pytest | ||
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1 +1 @@ | ||
distributionUrl=https://repo1.maven.org/maven2/org/apache/maven/apache-maven/3.8.1/apache-maven-3.8.1-bin.zip | ||
distributionUrl=https://repo1.maven.org/maven2/org/apache/maven/apache-maven/3.9.6/apache-maven-3.9.6-bin.zip |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,140 @@ | ||
.. _rstpython: | ||
|
||
################################### | ||
Working with Phenopackets in Python | ||
################################### | ||
|
||
Similarly to :ref:`Java <rstjava>`, the :ref:`Phenopacket Schema <rstschema>` can be considered the source of truth | ||
for the specification, and the JSON produced by an arbitrary implementation can be used to inter-operate | ||
with other services. Nevertheless, we **strongly** suggest to use the `phenopackets` library available | ||
from Python Package Index (PyPi) or use the Python bindings generated by Protobuf compiler from the Protobuf files. | ||
|
||
Here we provide a brief overview of the `phenopackets` library. | ||
|
||
|
||
Install `phenopackets` into your Python environment | ||
*************************************************** | ||
|
||
The `phenopackets` package can be installed from PyPi by running: | ||
|
||
.. code-block:: shell | ||
python3 -m pip install phenopackets | ||
We use `pip` to install `phenopackets` and the required libraries/dependencies. | ||
|
||
|
||
Create building blocks programmatically | ||
*************************************** | ||
|
||
Let's start by importing all building blocks of Phenopacket Schema v2: | ||
|
||
>>> import phenopackets.schema.v2 as pps2 | ||
|
||
Now we can access all building blocks of v2 Phenopacket Schema via `pps2` alias. | ||
|
||
For instance, we can create an :ref:`Ontology class <rstontologyclass>` that corresponds to a Human Phenotype Ontology | ||
term for *Spherocytosis* (`HP:0004444`): | ||
|
||
>>> spherocytosis = pps2.OntologyClass(id='HP:0004444', label='Spherocytosis') | ||
>>> spherocytosis # doctest: +NORMALIZE_WHITESPACE | ||
id: "HP:0004444" | ||
label: "Spherocytosis" | ||
|
||
All schema building blocks, including `OntologyClass`, are available under `pps2` alias, and can be created with constructors that accept key/value arguments. | ||
The constructors will not allow passing of arbitrary attributes: | ||
|
||
>>> pps2.OntologyClass(foo='bar') | ||
Traceback (most recent call last): | ||
... | ||
ValueError: Protocol message OntologyClass has no "foo" field. | ||
|
||
We do not have to provide all attributes at the creation time and we can set the fields sequentially | ||
using Python property syntax, to achieve the same outcome: | ||
|
||
>>> spherocytosis2 = pps2.OntologyClass() | ||
>>> spherocytosis2.id = 'HP:0004444' | ||
>>> spherocytosis2.label = 'Spherocytosis' | ||
>>> spherocytosis == spherocytosis2 | ||
True | ||
|
||
However, setting the field values with property syntax only works for | ||
`singular <https://protobuf.dev/reference/python/python-generated/#singular-fields-proto3>`_ (non-message) fields, | ||
such as `bool`, `int`, `str`, or `float`, and the assignment will *NOT* work for message fields: | ||
|
||
>>> pf = pps2.PhenotypicFeature() | ||
>>> pf.type = spherocytosis # doctest: +IGNORE_EXCEPTION_DETAIL | ||
Traceback (most recent call last): | ||
... | ||
AttributeError: Assignment not allowed to composite field "type" in protocol message object. | ||
|
||
To set a message field, we must use the `CopyFrom` function: | ||
|
||
>>> pf.type.CopyFrom(spherocytosis) | ||
>>> pf # doctest: +NORMALIZE_WHITESPACE | ||
type { | ||
id: "HP:0004444" | ||
label: "Spherocytosis" | ||
} | ||
|
||
Last, a repeated field can be set using list-like semantics: | ||
|
||
>>> modifiers = ( | ||
... pps2.OntologyClass(id='HP:0003623', label='Neonatal onset'), | ||
... pps2.OntologyClass(id='HP:0011010', label='Chronic'), | ||
... ) | ||
>>> pf.modifiers.extend(modifiers) | ||
>>> pf # doctest: +NORMALIZE_WHITESPACE | ||
type { | ||
id: "HP:0004444" | ||
label: "Spherocytosis" | ||
} | ||
modifiers { | ||
id: "HP:0003623" | ||
label: "Neonatal onset" | ||
} | ||
modifiers { | ||
id: "HP:0011010" | ||
label: "Chronic" | ||
} | ||
|
||
See `Protobuf documentation <https://protobuf.dev/reference/python/python-generated/#repeated-fields>`_ | ||
for more info. | ||
|
||
|
||
Building blocks I/O | ||
******************* | ||
|
||
Having an instance with data, we can write the content into Protobuf's wire format: | ||
|
||
>>> binary_str = pf.SerializeToString() | ||
>>> binary_str | ||
b'\x12\x1b\n\nHP:0004444\x12\rSpherocytosis*\x1c\n\nHP:0003623\x12\x0eNeonatal onset*\x15\n\nHP:0011010\x12\x07Chronic' | ||
|
||
and get the same content back: | ||
|
||
>>> pf2 = pps2.PhenotypicFeature() | ||
>>> _ = pf2.ParseFromString(binary_str) | ||
>>> pf == pf2 | ||
True | ||
|
||
We can also dump the content of the building block to a *JSON* string or to a `dict` with Python objects using | ||
`MessageToJson <https://googleapis.dev/python/protobuf/latest/google/protobuf/json_format.html#google.protobuf.json_format.MessageToJson>`_ | ||
or `MessageToDict <https://googleapis.dev/python/protobuf/latest/google/protobuf/json_format.html#google.protobuf.json_format.MessageToDict>`_ | ||
functions: | ||
|
||
>>> from google.protobuf.json_format import MessageToDict | ||
>>> json_dict = MessageToDict(pf) | ||
>>> json_dict | ||
{'type': {'id': 'HP:0004444', 'label': 'Spherocytosis'}, 'modifiers': [{'id': 'HP:0003623', 'label': 'Neonatal onset'}, {'id': 'HP:0011010', 'label': 'Chronic'}]} | ||
|
||
We complete the JSON round-trip using | ||
`Parse <https://googleapis.dev/python/protobuf/latest/google/protobuf/json_format.html#google.protobuf.json_format.Parse>`_ | ||
or `ParseDict <https://googleapis.dev/python/protobuf/latest/google/protobuf/json_format.html#google.protobuf.json_format.ParseDict>`_ | ||
functions: | ||
|
||
>>> from google.protobuf.json_format import ParseDict | ||
>>> pf2 = ParseDict(json_dict, pps2.PhenotypicFeature()) | ||
>>> pf == pf2 | ||
True | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.