Skip to content

Commit 8293dd5

Browse files
authored
Merge pull request #36 from octue/release/0.1.5
Release/0.1.5 Enabling task broker and improvements to CLI options
2 parents 1f8a434 + 01496ba commit 8293dd5

File tree

17 files changed

+301
-61
lines changed

17 files changed

+301
-61
lines changed

.github/ISSUE_TEMPLATE.md

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
2+
**I'm submitting a ...**
3+
- [ ] support request
4+
- [ ] bug report
5+
- [ ] feature request
6+
7+
Please fill out the relevant sections below.
8+
9+
---
10+
11+
## About Me
12+
13+
If you're new to the twined ecosystem, and you don't mind, it'd be great to hear a little bit of the wider context about
14+
you and what you're working on, so we can understand how best to help.
15+
16+
17+
18+
## Support request
19+
20+
- [ ] I've searched the list of existing issues
21+
- [ ] I expected the docs to cover it in [this section](please provide link to where you'd expect to find help on this in the docs) but they don't cover it.
22+
23+
I'm trying to [describe what you're trying to do].
24+
25+
26+
27+
## Feature request
28+
29+
### Use Case
30+
31+
Please [describe your motivation and use case].
32+
33+
### Current state
34+
35+
Please describe what you're doing presently to work around this or achieve what you're doing.
36+
37+
38+
39+
## Bug report
40+
41+
### What is the current behavior?
42+
43+
*If the current behavior is a bug, please provide any stack tracesthe steps to reproduce and if possible a minimal demo of the problem*
44+
45+
### What is the expected behavior?
46+
47+
### Your environment
48+
49+
- Library Version: x.y.z
50+
- Platform MacOS | Windows | Linux | Other
51+
52+
### Other information
53+
54+
Please give as much detail as you can, like:
55+
56+
- [ ] detailed explanation,
57+
- [ ] stacktraces
58+
- [ ] related issues
59+
- [ ] suggestions how to fix
60+
- [ ] links for us to have context, eg. stackoverflow, gitter, etc

.github/PULL_REQUEST_TEMPLATE.md

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
2+
<!-- PRs from release/x.y.z branches will be used to create Release Notes so make sure they contain everything -->
3+
<!-- Please don't add features not discussed in an issue first -->
4+
5+
## Contents
6+
7+
### New Features
8+
9+
<!-- Write `Closes #xyz` to link issues that this PR completes,
10+
`WIP on #xyz` to link issues that this PR works toward. -->
11+
12+
- [ ] Closes #xyz New thing for doing stuff
13+
- [ ] Closes #abc Another new thing for doing even more stuff
14+
15+
### Breaking changes
16+
17+
<!-- Breaking changes and how to work around them should be covered in the docs -->
18+
19+
- [ ] Breaks a thing
20+
- [ ] Breaks another thing
21+
22+
### Minor fixes and improvements
23+
24+
- [ ] Closes #pqr An annoying bug
25+
- [ ] Closes #stu A tweak
26+
27+
28+
## Quality Checklist
29+
30+
- [ ] New features are fully tested (No matter how much Coverage Karma you have)
31+
- [ ] **[v0.2 onward]** New features are included in the documentation
32+
- [ ] **[v0.2 onward]** Breaking changes are documented with clear instructions of what
33+
34+
### Coverage Karma
35+
36+
- [ ] If your PR decreases test coverage, do you feel you have built enough `Coverage Karma`* to justify it?
37+
38+
*Coverage Karma can be earned by disproportionally increasing test coverage in the rest of your contributions.
39+
It's an honesty policy - you keep count of your own.

.github/workflows/python-ci.yml

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
# This workflow will install Python dependencies, run tests and lint with a variety of Python versions.
2+
#
3+
# On successful test, the package will be published. For candidate releases, the package will be
4+
# published to test.pypi.org server (to ensure the process works). For merges to master, the
5+
# package will be published live.
6+
7+
name: python-ci
8+
9+
on: [push]
10+
11+
jobs:
12+
tests:
13+
runs-on: ubuntu-latest
14+
env:
15+
USING_COVERAGE: '3.8'
16+
strategy:
17+
matrix:
18+
python: [3.6, 3.7, 3.8]
19+
steps:
20+
- name: Checkout Repository
21+
uses: actions/checkout@v2
22+
- name: Setup Python
23+
uses: actions/setup-python@v2
24+
with:
25+
python-version: ${{ matrix.python }}
26+
- name: Install Tox and any other packages
27+
run: pip install tox
28+
- name: Run Tox
29+
run: tox -e py
30+
- name: Upload coverage to Codecov
31+
uses: codecov/codecov-action@v1
32+
with:
33+
file: coverage.xml
34+
fail_ci_if_error: true
35+
token: ${{ secrets.CODECOV_TOKEN }}
36+
37+
publish:
38+
if: contains(github.ref, 'main') || contains(github.ref, 'release/')
39+
runs-on: ubuntu-latest
40+
needs: tests
41+
steps:
42+
- name: Checkout Repository
43+
uses: actions/checkout@v2
44+
- name: Setup Python
45+
uses: actions/setup-python@v2
46+
with:
47+
python-version: 3.8
48+
- name: Make package
49+
run: |
50+
python3 -m pip install --upgrade setuptools wheel
51+
python3 setup.py sdist bdist_wheel
52+
- name: Test package is publishable with PyPI test server
53+
if: contains(github.ref, 'release/')
54+
uses: pypa/gh-action-pypi-publish@master
55+
with:
56+
user: __token__
57+
password: ${{ secrets.TEST_PYPI_TOKEN }}
58+
repository_url: https://test.pypi.org/legacy/
59+
skip_existing: true
60+
- name: Publish latest package to PyPI
61+
if: contains(github.ref, 'main')
62+
uses: pypa/gh-action-pypi-publish@master
63+
with:
64+
user: __token__
65+
password: ${{ secrets.PYPI_TOKEN }}
66+
verbose: true
67+

.travis.yml

Lines changed: 0 additions & 29 deletions
This file was deleted.

README.md

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
[![PyPI version](https://badge.fury.io/py/octue.svg)](https://badge.fury.io/py/octue)
2-
[![Build Status](https://travis-ci.com/octue/octue-sdk-python.svg?branch=master)](https://travis-ci.com/octue/octue-sdk-python)
32
[![codecov](https://codecov.io/gh/octue/octue-sdk-python/branch/master/graph/badge.svg?token=4KdR7fmwcT)](undefined)
43
[![Documentation Status](https://readthedocs.org/projects/octue/badge/?version=latest)](https://octue.readthedocs.io/en/latest/?badge=latest)
54
[![pre-commit](https://img.shields.io/badge/pre--commit-enabled-brightgreen?logo=pre-commit&logoColor=white)](https://github.com/pre-commit/pre-commit)

octue/__init__.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
from .cli import octue_cli
2-
from .runner import LOG_FORMAT, Runner
2+
from .logging_handlers import LOG_FORMAT
3+
from .runner import Runner
34

45

56
__all__ = "LOG_FORMAT", "octue_cli", "Runner"

octue/cli.py

Lines changed: 26 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -4,15 +4,20 @@
44
import pkg_resources
55

66
from octue.definitions import FOLDER_DEFAULTS, MANIFEST_FILENAME, VALUES_FILENAME
7+
from octue.logging_handlers import get_remote_handler
78
from octue.runner import Runner
89

910

11+
global_cli_context = {}
12+
13+
1014
@click.group(context_settings={"help_option_names": ["-h", "--help"]})
1115
@click.option(
1216
"--id",
1317
default=None,
18+
type=click.UUID,
1419
show_default=True,
15-
help="Id of the analysis being undertaken. None (for local use) will cause a unique ID to be generated.",
20+
help="UUID of the analysis being undertaken. None (for local use) will cause a unique ID to be generated.",
1621
)
1722
@click.option(
1823
"--skip-checks/--no-skip-checks",
@@ -22,6 +27,7 @@
2227
help="Skips the input checking. This can be a timesaver if you already checked "
2328
"data directories (especially if manifests are large).",
2429
)
30+
@click.option("--logger-uri", default=None, show_default=True, help="Stream logs to a websocket at the given URI.")
2531
@click.option(
2632
"--log-level",
2733
default="info",
@@ -37,16 +43,18 @@
3743
help="Forces a reset of analysis cache and outputs [For future use, currently not implemented]",
3844
)
3945
@click.version_option(version=pkg_resources.get_distribution("octue").version)
40-
@click.pass_context
41-
def octue_cli(ctx, id, skip_checks, log_level, force_reset):
46+
def octue_cli(id, skip_checks, logger_uri, log_level, force_reset):
4247
""" Octue CLI, enabling a data service / digital twin to be run like a command line application.
4348
4449
When acting in CLI mode, results are read from and written to disk (see
4550
https://octue-python-sdk.readthedocs.io/en/latest/ for how to run your application directly without the CLI).
4651
Once your application has run, you'll be able to find output values and manifest in your specified --output-dir.
4752
"""
48-
# TODO Forward command line options to runner via ctx
49-
ctx.ensure_object(dict)
53+
global_cli_context["analysis_id"] = id
54+
global_cli_context["skip_checks"] = skip_checks
55+
global_cli_context["logger_uri"] = logger_uri
56+
global_cli_context["log_level"] = log_level.upper()
57+
global_cli_context["force_reset"] = force_reset
5058

5159

5260
@octue_cli.command()
@@ -86,9 +94,7 @@ def octue_cli(ctx, id, skip_checks, log_level, force_reset):
8694
show_default=True,
8795
help="Directory to write outputs as files (overrides --data-dir).",
8896
)
89-
@click.option(
90-
"--twine", type=click.Path(), default="twine.json", show_default=True, help="Location of Twine file.",
91-
)
97+
@click.option("--twine", type=click.Path(), default="twine.json", show_default=True, help="Location of Twine file.")
9298
def run(app_dir, data_dir, config_dir, input_dir, output_dir, twine):
9399
config_dir = config_dir or os.path.join(data_dir, FOLDER_DEFAULTS["configuration"])
94100
input_dir = input_dir or os.path.join(data_dir, FOLDER_DEFAULTS["input"])
@@ -98,12 +104,24 @@ def run(app_dir, data_dir, config_dir, input_dir, output_dir, twine):
98104
twine=twine,
99105
configuration_values=os.path.join(config_dir, VALUES_FILENAME),
100106
configuration_manifest=os.path.join(config_dir, MANIFEST_FILENAME),
107+
log_level=global_cli_context["log_level"],
101108
)
109+
110+
if global_cli_context["logger_uri"]:
111+
handler = get_remote_handler(
112+
logger_uri=global_cli_context["logger_uri"], log_level=global_cli_context["log_level"]
113+
)
114+
else:
115+
handler = None
116+
102117
analysis = runner.run(
103118
app_src=app_dir,
119+
analysis_id=global_cli_context["analysis_id"],
120+
handler=handler,
104121
input_values=os.path.join(input_dir, VALUES_FILENAME),
105122
input_manifest=os.path.join(input_dir, MANIFEST_FILENAME),
106123
output_manifest_path=os.path.join(output_dir, MANIFEST_FILENAME),
124+
skip_checks=global_cli_context["skip_checks"],
107125
)
108126
analysis.finalise(output_dir=output_dir)
109127
return 0

octue/logging_handlers.py

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
import logging
2+
import logging.handlers
3+
from urllib.parse import urlparse
4+
5+
6+
# Logging format for analysis runs. All handlers should use this logging format, to make logs consistently parseable
7+
LOG_FORMAT = "%(name)s %(levelname)s %(asctime)s %(module)s %(process)d %(thread)d %(message)s"
8+
9+
10+
def get_default_handler(log_level):
11+
""" Gets a basic console handler set up for logging analyses
12+
"""
13+
console_handler = logging.StreamHandler()
14+
console_handler.setLevel(log_level)
15+
formatter = logging.Formatter(LOG_FORMAT)
16+
console_handler.setFormatter(formatter)
17+
return console_handler
18+
19+
20+
def get_remote_handler(logger_uri, log_level):
21+
"""Get a log handler for streaming logs to a remote URI accessed via HTTP or HTTPS."""
22+
parsed_uri = urlparse(logger_uri)
23+
24+
if parsed_uri.scheme not in {"ws", "wss"}:
25+
raise ValueError(
26+
f"Only WS and WSS protocols currently supported for remote logger URI. Received {logger_uri!r}."
27+
)
28+
29+
handler = logging.handlers.SocketHandler(host=parsed_uri.hostname, port=parsed_uri.port)
30+
handler.setLevel(log_level)
31+
handler.setFormatter(logging.Formatter(LOG_FORMAT))
32+
return handler

octue/resources/analysis.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ class Analysis(Identifiable, Loggable, Serialisable, Taggable):
4848
:parameter logger: Optional logging.Logger instance attached to the analysis
4949
"""
5050

51-
def __init__(self, twine, **kwargs):
51+
def __init__(self, twine, skip_checks=False, **kwargs):
5252
""" Constructor of Analysis instance
5353
"""
5454

@@ -57,6 +57,7 @@ def __init__(self, twine, **kwargs):
5757
twine = Twine(source=twine)
5858

5959
self.twine = twine
60+
self._skip_checks = skip_checks
6061

6162
# Pop any possible strand data sources before init superclasses (and tie them to protected attributes)
6263
strand_kwargs = ((name, kwargs.pop(name, None)) for name in ALL_STRANDS)

0 commit comments

Comments
 (0)