Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for GSF v3.09 #125

Merged
merged 16 commits into from
Mar 3, 2021
Merged
Show file tree
Hide file tree
Changes from 11 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@
build
_build
.cache
*.so

# Installer logs
pip-log.txt
Expand Down Expand Up @@ -38,6 +37,8 @@ MANIFEST.in
pip-wheel-metadata
/poetry.toml

notebooks/

# gsf
*.nsf
*.mbn21
3 changes: 3 additions & 0 deletions HISTORY.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
# History

## 2.0.0 (2021-02-24)
- Add support for GSF v3.09

## 1.6.0 (2020-06-24)
- Add environment variable `GSFPY_LIBGSF_PATH` to enable swapping out libgsf at runtime

Expand Down
16 changes: 11 additions & 5 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ checktypes: .venv ## check types with mypy
poetry run mypy --ignore-missing-imports gsfpy tests

checkstyle: .venv ## check style with flake8 and black
poetry run flake8 gsfpy tests
poetry run flake8 --ignore F401,F403,F405 gsfpy tests
poetry run isort --check-only --profile black gsfpy tests
poetry run black --check --diff gsfpy tests

Expand All @@ -68,10 +68,16 @@ checklicenses: .venv requirements.txt ## check dependencies meet licence rules
poetry run liccheck -s liccheck.ini

test: .venv ## run tests quickly with the default Python
poetry run pytest tests/test_libgsf_load_valid.py --verbose --capture=no
poetry run pytest tests/test_libgsf_load_invalid.py --verbose --capture=no
poetry run pytest tests/test_libgsf_load_default.py --verbose --capture=no
poetry run pytest --ignore-glob=tests/test_libgsf_load_*.py --verbose --capture=no
poetry run pytest --ignore-glob=tests/gsfpy3_08/* --ignore-glob=tests/gsfpy3_09/* --ignore-glob=tests/gsfpy/test_gsffile_with_*.py --verbose --capture=no
poetry run pytest --ignore-glob=tests/gsfpy3_08/* --ignore-glob=tests/gsfpy3_09/* --ignore-glob=tests/gsfpy/test_gsffile.py --verbose --capture=no
poetry run pytest tests/gsfpy3_08/test_libgsf_load_valid.py --verbose --capture=no
poetry run pytest tests/gsfpy3_08/test_libgsf_load_invalid.py --verbose --capture=no
poetry run pytest tests/gsfpy3_08/test_libgsf_load_default.py --verbose --capture=no
poetry run pytest --ignore-glob=tests/gsfpy3_08/test_libgsf_load_*.py --ignore-glob=tests/gsfpy3_09/* --verbose --capture=no --cov=gsfpy3_08 --cov-fail-under=95 --cov-config=tox.ini
poetry run pytest tests/gsfpy3_09/test_libgsf_load_valid.py --verbose --capture=no
poetry run pytest tests/gsfpy3_09/test_libgsf_load_invalid.py --verbose --capture=no
poetry run pytest tests/gsfpy3_09/test_libgsf_load_default.py --verbose --capture=no
poetry run pytest --ignore-glob=tests/gsfpy3_09/test_libgsf_load_*.py --ignore-glob=tests/gsfpy3_08/* --verbose --capture=no --cov=gsfpy3_09 --cov-fail-under=95 --cov-config=tox.ini
Comment on lines +74 to +83

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there a reason these are run individually rather than just pointing pytest at the tests folder?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is a reason! Once a module is loaded in python it is almost impossible to unload it. Therefore for tests which are checking the gsfpy behaviour of loading the correct version-specific namespace based on the DEFAULT_GSF_VERSION env variable, or loading custom versions of the libgsf shared object libraries, it is necessary to execute these in their own dedicated pytest runs.


test-all: .venv ## run tests on every Python version with tox
poetry run tox
Expand Down
73 changes: 40 additions & 33 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,21 @@
Python wrapper for the C implementation of the Generic Sensor Format library.

- Free software: MIT license
- __Notes on licensing__: The bundled `libgsf/libgsf03_08.so` is covered by the [LGPL v2.1](https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html) license. A copy of this license is included in the project at `libgsf/libgsf_LICENSE.md`. This does not affect the top-level MIT licensing of the project. However, as required by the libgsf license, the libgsf shared object library used by gsfpy at runtime may be replaced with a different version by setting the `GSFPY_LIBGSF_PATH` environment variable to the absolute file path of the new library.
- __Notes on licensing__: The bundled `gsfpy3_0x/libgsf/libgsf03_0x.so` binaries are covered by the [LGPL v2.1](https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html) license. Copies of this license are included in the project at `gsfpy3_0x/libgsf/libgsf_LICENSE.md`. The top-level MIT licensing of the overall `gsfpy` project is not affected by this. However, as required by the libgsf license, the libgsf shared object libraries used by the `gsfpy3_0x` packages at runtime may be replaced with a different version by setting the `GSFPY3_08_LIBGSF_PATH` and/or `GSFPY3_09_LIBGSF_PATH` environment variables to the absolute file path of the new library.

## Namespaces and supported GSF versions
The `gsfpy` package provides three namespaces: `gsfpy`, `gsfpy3_08` and `gsfpy3_09`.

The default version of GSF supported is `3.08`. Top level package functionality for `3.08` can be used either via `import gsfpy` (without setting the `DEFAULT_GSF_VERSION` environment variable - see below) or `import gsfpy3_08`. Note that `import gsfpy` will also work for versions 3.06 and 3.07 of GSF as well (older versions have not been tested).

If you are using GSF v3.09, there are two options:
* Set the `DEFAULT_GSF_VERSION` environment variable to `"3.09"`, then `import gsfpy`
* Import the 3.09 package directly with `import gsfpy3_09`


## Features

- gsfpy.bindings provides wrappers for all GSFlib functions, including I/O, utility and info functions.
- The `gsfpy(3_0x).bindings` modules provide wrappers for all GSFlib functions, including I/O, utility and info functions.
Minor exceptions are noted in the sections below.

- For added convenience the gsfpy top level package provides the following higher level abstractions:
Expand All @@ -29,22 +39,22 @@ pip install gsfpy
```
#### From GitHub (SSH)
```shell script
pip install git+ssh://[email protected]/UKHO/gsfpy.git@master
pip install git+ssh://[email protected]/UKHO/gsfpy3_08.git@master
```
#### From GitHub (HTTPS)
```shell script
pip install git+https://github.com/UKHO/gsfpy.git@master
pip install git+https://github.com/UKHO/gsfpy3_08.git@master

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are these path's correct?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed

```

## Examples of usage

### Open/close/read from a GSF file
### Open/close/read from a GSF file (GSF v3.08)

```python
from ctypes import string_at

from gsfpy import open_gsf
from gsfpy.enums import RecordType
from gsfpy3_08 import open_gsf
from gsfpy3_08.enums import RecordType

with open_gsf("path/to/file.gsf") as gsf_file:
# Note - file is closed automatically upon exiting 'with' block
Expand All @@ -55,16 +65,16 @@ with open_gsf("path/to/file.gsf") as gsf_file:
print(string_at(record.comment.comment))
```

### Write to a GSF file
### Write to a GSF file (GSF v3.09)

```python
from ctypes import c_int, create_string_buffer

from gsfpy import open_gsf
from gsfpy.enums import FileMode, RecordType
from gsfpy.gsfRecords import c_gsfRecords
from gsfpy3_09 import open_gsf
from gsfpy3_09.enums import FileMode, RecordType
from gsfpy3_09.gsfRecords import c_gsfRecords

comment = "My comment"
comment = b"My comment"

# Initialize the contents of the record that will be written.
# Note use of ctypes.create_string_buffer() to set POINTER(c_char) contents.
Expand All @@ -77,32 +87,29 @@ with open_gsf("path/to/file.gsf", mode=FileMode.GSF_CREATE) as gsf_file:
gsf_file.write(record, RecordType.GSF_RECORD_COMMENT)
```

### Copy GSF records
### Copy GSF records (GSF v3.08 as default)

```python
from ctypes import byref, c_int, pointer

import gsfpy
from gsfpy.enums import FileMode, RecordType
from gsfpy.gsfDataID import c_gsfDataID
from gsfpy.gsfRecords import c_gsfRecords
from gsfpy import *


# This example uses gsfpy.bindings to illustrate use of the lower level functions
# This example uses the bindings module to illustrate use of the lower level functions
file_handle = c_int(0)
data_id = c_gsfDataID()
source_records = c_gsfRecords()
target_records = c_gsfRecords()
data_id = gsfDataID.c_gsfDataID()
source_records = gsfRecords.c_gsfRecords()
target_records = gsfRecords.c_gsfRecords()

ret_val_open = gsfpy.bindings.gsfOpen(
"path/to/file.gsf", FileMode.GSF_READONLY, byref(file_handle)
ret_val_open = bindings.gsfOpen(
b"path/to/file.gsf", enums.FileMode.GSF_READONLY, byref(file_handle)
)

# Note use of ctypes.byref() as a shorthand way of passing POINTER parameters to
# the underlying foreign function call. ctypes.pointer() may also be used.
bytes_read = gsfpy.bindings.gsfRead(
file_handle,
RecordType.GSF_RECORD_COMMENT,
enums.RecordType.GSF_RECORD_COMMENT,
byref(data_id),
byref(source_records),
)
Expand All @@ -112,23 +119,23 @@ bytes_read = gsfpy.bindings.gsfRead(
# the native underlying function causes memory ownership clashes. byref()
# is only suitable for passing parameters to foreign function calls (see
# ctypes docs).
ret_val_cpy = gsfpy.bindings.gsfCopyRecords(
ret_val_cpy = bindings.gsfCopyRecords(
pointer(target_records), pointer(source_records)
)
ret_val_close = gsfpy.bindings.gsfClose(file_handle)
ret_val_close = bindings.gsfClose(file_handle)
```

### Troubleshoot

```python
import gsfpy
from gsfpy3_09.bindings import gsfIntError, gsfStringError

# The gsfIntError() and gsfStringError() functions are useful for
# diagnostics. They return an error code and corresponding error
# message, respectively.
retValIntError = gsfpy.bindings.gsfIntError()
retValStringError = gsfpy.bindings.gsfStringError()
print(retValStringError)
retValIntError = gsfIntError()
retValStringError = gsfStringError()
print(retValIntError, retValStringError)
```

## Notes on implementation
Expand Down Expand Up @@ -174,7 +181,7 @@ More recent versions of these documents can be downloaded from the
By default Poetry will create it's own virtual environment using your system's Python. [This feature can be disabled.](https://python-poetry.org/docs/faq/#i-dont-want-poetry-to-manage-my-virtual-environments-can-i-disable-it)

```shell script
git clone [email protected]:UKHO/gsfpy.git
git clone [email protected]:UKHO/gsfpy3_08.git

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As above

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed

cd gsfpy
poetry install
```
Expand All @@ -184,7 +191,7 @@ poetry install
A good choice if you want to run a version of Python different than available through your system's package manager

```shell script
git clone [email protected]:UKHO/gsfpy.git
git clone [email protected]:UKHO/gsfpy3_08.git

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As above

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed

cd gsfpy
pyenv install 3.8.3
pyenv virtualenv 3.8.3 gsfpy
Expand Down Expand Up @@ -216,7 +223,7 @@ calling gsfpy to assess these risks and mitigate where deemed necessary.
GSF data processed using gsfpy should be sourced from reliable providers
and checked for integrity where possible.

Please also refer to the LICENSE file for the terms of use of gsfpy.
Please also refer to the LICENSE file for the terms of use of gsfpy3_08.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As above

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed


## Credits

Expand Down
10 changes: 2 additions & 8 deletions gsfpy/GSF_POSITION.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,3 @@
from ctypes import Structure, c_double
from gsfpy import mirror_default_gsf_version_submodule


class c_GSF_POSITION(Structure):
_fields_ = [
("lon", c_double),
("lat", c_double),
("z", c_double),
]
mirror_default_gsf_version_submodule(globals(), "GSF_POSITION")
12 changes: 2 additions & 10 deletions gsfpy/GSF_POSITION_OFFSETS.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,3 @@
from ctypes import Structure, c_double
from gsfpy import mirror_default_gsf_version_submodule


# Note: the coordinate system is:
# +x forward, +y starboard, + z down, +hdg cw from north
class c_GSF_POSITION_OFFSETS(Structure):
_fields_ = [
("x", c_double),
("y", c_double),
("z", c_double),
]
mirror_default_gsf_version_submodule(globals(), "GSF_POSITION_OFFSETS")
Loading