Skip to content
Snippets Groups Projects
Commit fc7bd63b authored by Taya Snijder's avatar Taya Snijder
Browse files

Initial commit

parents
No related branches found
No related tags found
No related merge requests found
Pipeline #43158 failed
root = true
[*]
charset = utf-8
end_of_line = lf
indent_size = 4
indent_style = space
insert_final_newline = true
trim_trailing_whitespace = true
max_line_length = 120
tab_width = 4
ij_continuation_indent_size = 8
ij_formatter_off_tag = @formatter:off
ij_formatter_on_tag = @formatter:on
ij_formatter_tags_enabled = false
ij_smart_tabs = false
ij_visual_guides = none
ij_wrap_on_typing = false
[{*.bash,*.sh,*.zsh}]
indent_size = 2
tab_width = 2
ij_shell_binary_ops_start_line = false
ij_shell_keep_column_alignment_padding = false
ij_shell_minify_program = false
ij_shell_redirect_followed_by_space = false
ij_shell_switch_cases_indented = false
ij_shell_use_unix_line_separator = true
[{*.har,*.jsb2,*.jsb3,*.json,.babelrc,.eslintrc,.stylelintrc,bowerrc,jest.config}]
indent_size = 2
ij_json_array_wrapping = split_into_lines
ij_json_keep_blank_lines_in_code = 0
ij_json_keep_indents_on_empty_lines = false
ij_json_keep_line_breaks = true
ij_json_keep_trailing_comma = false
ij_json_object_wrapping = split_into_lines
ij_json_property_alignment = do_not_align
ij_json_space_after_colon = true
ij_json_space_after_comma = true
ij_json_space_before_colon = false
ij_json_space_before_comma = false
ij_json_spaces_within_braces = false
ij_json_spaces_within_brackets = false
ij_json_wrap_long_lines = false
[{*.markdown,*.md}]
ij_markdown_force_one_space_after_blockquote_symbol = true
ij_markdown_force_one_space_after_header_symbol = true
ij_markdown_force_one_space_after_list_bullet = true
ij_markdown_force_one_space_between_words = true
ij_markdown_insert_quote_arrows_on_wrap = true
ij_markdown_keep_indents_on_empty_lines = false
ij_markdown_keep_line_breaks_inside_text_blocks = true
ij_markdown_max_lines_around_block_elements = 1
ij_markdown_max_lines_around_header = 1
ij_markdown_max_lines_between_paragraphs = 1
ij_markdown_min_lines_around_block_elements = 1
ij_markdown_min_lines_around_header = 1
ij_markdown_min_lines_between_paragraphs = 1
ij_markdown_wrap_text_if_long = true
ij_markdown_wrap_text_inside_blockquotes = true
[{*.py,*.pyw}]
max_line_length = 88
ij_python_align_collections_and_comprehensions = true
ij_python_align_multiline_imports = true
ij_python_align_multiline_parameters = true
ij_python_align_multiline_parameters_in_calls = false
ij_python_blank_line_at_file_end = true
ij_python_blank_lines_after_imports = 1
ij_python_blank_lines_after_local_imports = 0
ij_python_blank_lines_around_class = 1
ij_python_blank_lines_around_method = 1
ij_python_blank_lines_around_top_level_classes_functions = 2
ij_python_blank_lines_before_first_method = 0
ij_python_call_parameters_new_line_after_left_paren = true
ij_python_call_parameters_right_paren_on_new_line = true
ij_python_call_parameters_wrap = normal
ij_python_dict_alignment = 2
ij_python_dict_new_line_after_left_brace = true
ij_python_dict_new_line_before_right_brace = true
ij_python_dict_wrapping = 1
ij_python_from_import_new_line_after_left_parenthesis = true
ij_python_from_import_new_line_before_right_parenthesis = true
ij_python_from_import_parentheses_force_if_multiline = true
ij_python_from_import_trailing_comma_if_multiline = false
ij_python_from_import_wrapping = 1
ij_python_hang_closing_brackets = false
ij_python_keep_blank_lines_in_code = 1
ij_python_keep_blank_lines_in_declarations = 1
ij_python_keep_indents_on_empty_lines = false
ij_python_keep_line_breaks = true
ij_python_method_parameters_new_line_after_left_paren = true
ij_python_method_parameters_right_paren_on_new_line = true
ij_python_method_parameters_wrap = normal
ij_python_new_line_after_colon = true
ij_python_new_line_after_colon_multi_clause = true
ij_python_optimize_imports_always_split_from_imports = false
ij_python_optimize_imports_case_insensitive_order = false
ij_python_optimize_imports_join_from_imports_with_same_source = false
ij_python_optimize_imports_sort_by_type_first = true
ij_python_optimize_imports_sort_imports = true
ij_python_optimize_imports_sort_names_in_from_imports = false
ij_python_space_after_comma = true
ij_python_space_after_number_sign = true
ij_python_space_after_py_colon = true
ij_python_space_before_backslash = true
ij_python_space_before_comma = false
ij_python_space_before_for_semicolon = false
ij_python_space_before_lbracket = false
ij_python_space_before_method_call_parentheses = false
ij_python_space_before_method_parentheses = false
ij_python_space_before_number_sign = true
ij_python_space_before_py_colon = false
ij_python_space_within_empty_method_call_parentheses = false
ij_python_space_within_empty_method_parentheses = false
ij_python_spaces_around_additive_operators = true
ij_python_spaces_around_assignment_operators = true
ij_python_spaces_around_bitwise_operators = true
ij_python_spaces_around_eq_in_keyword_argument = false
ij_python_spaces_around_eq_in_named_parameter = false
ij_python_spaces_around_equality_operators = true
ij_python_spaces_around_multiplicative_operators = true
ij_python_spaces_around_power_operator = false
ij_python_spaces_around_relational_operators = true
ij_python_spaces_around_shift_operators = true
ij_python_spaces_within_braces = false
ij_python_spaces_within_brackets = false
ij_python_spaces_within_method_call_parentheses = false
ij_python_spaces_within_method_parentheses = false
ij_python_use_continuation_indent_for_arguments = true
ij_python_use_continuation_indent_for_collection_and_comprehensions = false
ij_python_use_continuation_indent_for_parameters = true
ij_python_wrap_long_lines = false
[{*.yaml,*.yml}]
indent_size = 2
ij_yaml_align_values_properties = do_not_align
ij_yaml_autoinsert_sequence_marker = true
ij_yaml_block_mapping_on_new_line = false
ij_yaml_indent_sequence_value = true
ij_yaml_keep_indents_on_empty_lines = false
ij_yaml_keep_line_breaks = true
ij_yaml_sequence_on_new_line = false
ij_yaml_space_before_colon = false
ij_yaml_spaces_within_braces = true
ij_yaml_spaces_within_brackets = true
dist/*
*.egg-info
*.pyc
.coverage
coverage.xml
htmlcov/*
# Setuptools SCM
attribute_wrapper/_version.py
# IDE configuration
.vscode
.idea
default:
image: python:3.10 # use latest for building/linting
before_script:
- python --version # For debugging
- python -m pip install --upgrade pip
- pip install --upgrade tox twine
cache:
paths:
- .cache/pip
# Do not cache .tox, to recreate virtualenvs for every step
stages:
- lint
# check if this needs to be a separate step
# - build_extensions
- test
- package
- integration
- publish # publish instead of deploy
# Caching of dependencies to speed up builds
variables:
PIP_CACHE_DIR: "$CI_PROJECT_DIR/.cache/pip"
run_black:
stage: lint
script:
- tox -e black
allow_failure: true
run_flake8:
stage: lint
script:
- tox -e pep8
allow_failure: true
run_pylint:
stage: lint
script:
- tox -e pylint
allow_failure: true
# build_extensions:
# stage: build_extensions
# script:
# - echo "build fortran/c/cpp extension source code"
run_unit_tests_coverage:
stage: test
image: python:3.7
script:
- tox -e coverage
artifacts:
reports:
coverage_report:
coverage_format: cobertura
path: coverage.xml
paths:
- htmlcov/*
run_unit_tests:
stage: test
image: python:3.${PY_VERSION}
script:
- tox -e py3${PY_VERSION}
parallel:
matrix: # use the matrix for testing
- PY_VERSION: [7, 8, 9, 10]
package_files:
stage: package
artifacts:
expire_in: 1w
paths:
- dist/*
script:
- tox -e build
package_docs:
stage: package
artifacts:
expire_in: 1w
paths:
- docs/* # update path to match the dest dir for documentation
script:
- echo "build and collect docs"
run_integration_tests:
stage: integration
needs:
- package_files
script:
- echo "make sure to move out of source dir"
- echo "install package from filesystem (or use the artefact)"
- echo "run against foreign systems (e.g. databases, cwl etc.)"
publish_on_gitlab:
stage: publish
environment: gitlab
needs:
- package_files
when: manual
rules:
- if: $CI_COMMIT_TAG
script:
- echo "run twine for gitlab"
- |
TWINE_PASSWORD=${CI_JOB_TOKEN} \
TWINE_USERNAME=gitlab-ci-token \
python -m twine upload \
--repository-url ${CI_API_V4_URL}/projects/${CI_PROJECT_ID}/packages/pypi dist/*
publish_on_test_pypi:
stage: publish
environment: pypi-test
needs:
- package_files
when: manual
rules:
- if: $CI_COMMIT_TAG
script:
- echo "run twine for test pypi"
# - |
# TWINE_PASSWORD=${PIPY_TOKEN} \
# TWINE_USERNAME=${PIPY_USERNAME} \
# TODO: replace URL with a pipy URL
# python -m twine upload \
# --repository-url ${CI_API_V4_URL}/projects/${CI_PROJECT_ID}/packages/pypi dist/*
publish_on_pypi:
stage: publish
environment: pypi
needs:
- package_files
when: manual
rules:
- if: $CI_COMMIT_TAG
script:
- echo "run twine for pypi"
# - |
# TWINE_PASSWORD=${PIPY_TOKEN} \
# TWINE_USERNAME=${PIPY_USERNAME} \
# TODO: replace URL with a pipy URL
# python -m twine upload \
# --repository-url ${CI_API_V4_URL}/projects/${CI_PROJECT_ID}/packages/pypi dist/*
publish_to_readthedocs:
stage: publish
environment: readthedocs
needs:
- package_docs
when: manual
rules:
- if: $CI_COMMIT_TAG
script:
- echo "scp docs/* ???"
""" My Awesome Package """
try:
from importlib import metadata
except ImportError: # for Python<3.8
import importlib_metadata as metadata
__version__ = metadata.version("AttributeWrapper")
# Copyright (C) 2022 ASTRON (Netherlands Institute for Radio Astronomy)
# SPDX-License-Identifier: Apache-2.0
import logging
from functools import reduce
from operator import mul
import numpy
from tango import AttrWriteType, AttReqType
from tango.server import attribute
logger = logging.getLogger()
__all__ = ["AttributeWrapper"]
def fault_on_error():
"""
Wrapper to catch exceptions. Sets the device in a FAULT state if any occurs.
"""
def inner(func):
@wraps(func)
def error_wrapper(device, *args, **kwargs):
try:
return func(device, *args, **kwargs)
except Exception as e:
logger.exception("Function failed.")
device.Fault(f"FAULT in {func.__name__}: {e.__class__.__name__}: {e}")
raise
return error_wrapper
return inner
class AttributeIO(object):
"""Holds the I/O functionality for an attribute for a specific device."""
def __init__(self, device, attribute_wrapper):
# Link to the associated device
self.device = device
# Link to the associated attribute wrapper
self.attribute_wrapper = attribute_wrapper
# Link to last (written) value
self.cached_value = None
# Specific read and write functions for this attribute on this device
self.read_function = lambda: None
self.write_function = lambda value: None
def cached_read_function(self):
"""Return the last (written) value, if available. Otherwise, read
from the device."""
if self.cached_value is not None:
return self.cached_value
self.cached_value = self.read_function()
return self.cached_value
def cached_write_function(self, value):
"""Writes the given value to the device, and updates the cache."""
# flexible array sizes are not supported by all clients. make sure we only write arrays of maximum size.
if self.attribute_wrapper.shape != ():
if isinstance(value, numpy.ndarray):
value_shape = value.shape
else:
if len(value) > 0 and isinstance(value[0], list):
# nested list
value_shape = (len(value), len(value[0]))
else:
# straight list
value_shape = (len(value),)
if value_shape != self.attribute_wrapper.shape:
raise ValueError(
f"Tried writing an array of shape {value_shape} into an attribute of shape {self.attribute_wrapper.shape}"
)
self.write_function(value)
self.cached_value = value
class AttributeWrapper(attribute):
"""
Wraps all the attributes in a wrapper class to manage most of the redundant code behind the scenes
"""
def __init__(
self,
comms_id=None,
comms_annotation=None,
datatype=None,
dims=(1,),
access=AttrWriteType.READ,
**kwargs,
):
"""
wraps around the tango Attribute class. Provides an easier interface for 1d or 2d arrays. Also provides a way to abstract
managing the communications interface.
comms_id: user-supplied identifier that is attached to this object, to identify which communication class will need to be attached
comms_annotation: data passed along to the attribute. can be given any form of data. handling is up to client implementation
datatype: any numpy datatype
dims: dimensions of the attribute as a tuple, or (1,) for a scalar.
init_value: value
"""
# ensure the type is a numpy array.
# see also https://pytango.readthedocs.io/en/stable/server_api/server.html?highlight=devlong#module-tango.server for
# more details about type conversion Python/numpy -> PyTango
if "numpy" not in str(datatype) and datatype != str and datatype != bool:
raise ValueError(
f"Attribute needs to be a Tango-supported numpy, str or bool type, but has type {datatype}"
)
self.comms_id = comms_id # store data that can be used to identify the comms interface to use. not used by the wrapper itself
self.comms_annotation = comms_annotation # store data that can be used by the comms interface. not used by the wrapper itself
self.datatype = datatype
if dims == (1,):
# scalar
# Tango defines a scalar as having dimensions (1,0), see https://pytango.readthedocs.io/en/stable/server_api/attribute.html
max_dim_x = 1
max_dim_y = 0
dtype = datatype
shape = ()
elif len(dims) == 1:
# spectrum
max_dim_x = dims[0]
max_dim_y = 0
dtype = (datatype,)
shape = (max_dim_x,)
elif len(dims) == 2:
# image
max_dim_x = dims[1]
max_dim_y = dims[0]
dtype = ((datatype,),)
shape = (max_dim_y, max_dim_x)
else:
# higher dimensional
# >2D arrays collapse into the X and Y dimensions. The Y (major) dimension mirrors the first dimension given, the
# rest collapses into the X (minor) dimension.
max_dim_x = reduce(mul, dims[1:])
max_dim_y = dims[0]
dtype = ((datatype,),)
shape = (max_dim_y, max_dim_x)
# actual shape of the data as it is read/written
self.shape = shape
if access == AttrWriteType.READ_WRITE:
"""If the attribute is of READ_WRITE type, assign the write and read functions to it"""
# we return the last written value, as we are the only ones in control,
# and the hardware does not necessarily return what we've written
# (see L2SDP-725).
@fault_on_error()
def write_func_wrapper(device, value):
"""
write_func_wrapper writes a value to this attribute
"""
try:
io = self.get_attribute_io(device)
return io.cached_write_function(value)
except Exception as e:
raise e.__class__(
f"Could not write attribute {comms_annotation}"
) from e
@fault_on_error()
def read_func_wrapper(device):
"""
read_func_wrapper reads the attribute value, stores it and returns it"
"""
# lofar.read_attribute ignores fisallowed. So check again if we're allowed to read.
if not device.is_attribute_access_allowed(AttReqType.READ_REQ):
return None
try:
io = self.get_attribute_io(device)
return io.cached_read_function()
except Exception as e:
raise e.__class__(
f"Could not read attribute {comms_annotation}"
) from e
self.fset = write_func_wrapper
self.fget = read_func_wrapper
else:
"""Assign the read function to the attribute"""
@fault_on_error()
def read_func_wrapper(device):
"""
read_func_wrapper reads the attribute value, stores it and returns it"
"""
# lofar.read_attribute ignores fisallowed. So check again if we're allowed to read.
if not device.is_attribute_access_allowed(AttReqType.READ_REQ):
return None
try:
io = self.get_attribute_io(device)
return io.read_function()
except Exception as e:
raise e.__class__(
f"Could not read attribute {comms_annotation}"
) from e
self.fget = read_func_wrapper
# "fisallowed" is called to ask us whether an attribute can be accessed. If not, the attribute won't be accessed,
# and the cache not updated. This forces Tango to also force a read the moment an attribute does become accessible.
# The provided function will be used with the call signature "(device: Device, req_type: AttReqType) -> bool".
#
# NOTE: fisallowed=<callable> does not work: https://gitlab.com/tango-controls/pytango/-/issues/435
# So we have to use fisallowed=<str> here, which causes the function device.<str> to be called.
super().__init__(
dtype=dtype,
max_dim_y=max_dim_y,
max_dim_x=max_dim_x,
access=access,
fisallowed="is_attribute_access_allowed",
format=str(dims),
**kwargs,
)
def get_attribute_io(self, device):
"""
returns the attribute I/O functions from a certain device, or registers it if not present
"""
try:
return device._attribute_wrapper_io[self]
except KeyError:
device._attribute_wrapper_io[self] = AttributeIO(device, self)
return device._attribute_wrapper_io[self]
def set_comm_client(self, device, client):
"""
takes a communications client as input arguments This client should be of a class containing a "get_mapping" function
and return a read and write function that the wrapper will use to get/set data.
"""
try:
read_attr_func, write_attr_func = client.setup_attribute(
self.comms_annotation, self
)
io = self.get_attribute_io(device)
io.read_function = read_attr_func
io.write_function = write_attr_func
except Exception as e:
raise Exception(
f"Exception while setting {client.__class__.__name__} attribute with annotation: '{self.comms_annotation}'"
) from e
async def async_set_comm_client(self, device, client):
"""
Asynchronous version of set_comm_client.
"""
try:
read_attr_func, write_attr_func = await client.setup_attribute(
self.comms_annotation, self
)
io = self.get_attribute_io(device)
io.read_function = read_attr_func
io.write_function = write_attr_func
except Exception as e:
raise Exception(
f"Exception while setting {client.__class__.__name__} attribute with annotation: '{self.comms_annotation}'"
) from e
def set_pass_func(self, device):
logger.debug(
"using pass function for attribute with annotation: {}".format(
self.comms_annotation
)
)
io = self.get_attribute_io(device)
io.read_function = lambda: None
io.write_function = lambda value: None
""" Cool module containing functions, classes and other useful things """
def greeter():
"""Prints a nice message"""
print("Hello World!")
LICENSE 0 → 100644
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
include LICENSE
include README.md
recursive-include docs *
recursive-exclude tests *
# Example Python Package
An example repository of an CI/CD pipeline for building, testing and publishing a python package.
If you find some missing functionality with regards to CI/CD, testing, linting or something else, feel free to make a merge request with the proposed changes.
## Example of README.md contents below:
## Installation
```
pip install .
```
## Usage
```
from my_awesome_app import cool_module
cool_module.greeter() # prints "Hello World"
```
## Contributing
To contribute, please create a feature branch and a "Draft" merge request.
Upon completion, the merge request should be marked as ready and a reviewer
should be assigned.
Verify your changes locally and be sure to add tests. Verifying local
changes is done through `tox`.
```pip install tox```
With tox the same jobs as run on the CI/CD pipeline can be ran. These
include unit tests and linting.
```tox```
To automatically apply most suggested linting changes execute:
```tox -e format```
## License
This project is licensed under the Apache License Version 2.0
[build-system]
requires = [
"setuptools>=45",
"setuptools_scm[toml]>=6.2",
"wheel"
]
build-backend = "setuptools.build_meta"
[tool.setuptools_scm]
write_to = "attribute_wrapper/_version.py"
[tool.pylint]
ignore = "_version.py"
numpy >= 1.20.0 # BSD
[metadata]
name = AttributeWrapper
description = #TODO
long_description = file: README.md
long_description_content_type = text/markdown
url = https://git.astron.nl/templates/python-package
license = Apache License 2.0
classifiers =
Development Status :: 3 - Alpha
Environment :: Web Environment
Intended Audience :: Developers
Intended Audience :: Science/Research
License :: OSI Approved :: Apache Software License
Operating System :: OS Independent
Programming Language :: Python
Programming Language :: Python :: 3
Programming Language :: Python :: 3 :: Only
Programming Language :: Python :: 3.7
Programming Language :: Python :: 3.8
Programming Language :: Python :: 3.9
Programming Language :: Python :: 3.10
Topic :: Internet :: WWW/HTTP
Topic :: Internet :: WWW/HTTP :: Dynamic Content
Topic :: Scientific/Engineering
Topic :: Scientific/Engineering :: Astronomy
[options]
include_package_data = true
packages = find:
python_requires = >=3.7
install_requires =
importlib-metadata>=0.12;python_version<"3.8"
numpy
[flake8]
max-line-length = 88
extend-ignore = E203
import setuptools
setuptools.setup()
autopep8 >= 1.7.0 # MIT
black >= 22.0.0 # MIT
build >= 0.8.0 # MIT
flake8 >= 5.0.0 # MIT
pylint >= 2.15.0 # GPLv2
pytest >= 7.0.0 # MIT
pytest-cov >= 3.0.0 # MIT
This diff is collapsed.
tox.ini 0 → 100644
[tox]
# Generative environment list to test all supported Python versions
envlist = py3{7,8,9,10},black,pep8,pylint
minversion = 3.18.0
# Source distributions are explicitly build using tox -e build
skipsdist = True
[testenv]
usedevelop = True
setenv =
LANGUAGE=en_US
LC_ALL=en_US.UTF-8
PYTHONWARNINGS=default::DeprecationWarning
deps =
-r{toxinidir}/requirements.txt
-r{toxinidir}/tests/requirements.txt
commands =
{envpython} --version
{envpython} -m pytest
[testenv:coverage]
commands =
{envpython} --version
{envpython} -m pytest --cov-report xml --cov-report html --cov=map
# Use generative name and command prefixes to reuse the same virtualenv
# for all linting jobs.
[testenv:{pep8,black,pylint,format}]
usedevelop = False
envdir = {toxworkdir}/linting
commands =
pep8: {envpython} -m flake8 --version
pep8: {envpython} -m flake8 --extend-exclude './.venv/','./venv/'
black: {envpython} -m black --version
black: {envpython} -m black --check --diff .
pylint: {envpython} -m pylint --version
pylint: {envpython} -m pylint my_awesome_app tests
format: {envpython} -m autopep8 -v -aa --in-place --recursive my_awesome_app
format: {envpython} -m autopep8 -v -aa --in-place --recursive tests
format: {envpython} -m black -v .
[testenv:build]
usedevelop = False
commands = {envpython} -m build
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment