Skip to content

Minor issue fixes and test reorganisation #388

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

Merged
merged 13 commits into from
Apr 28, 2020
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ pip install odml
## Tutorial and examples

- We have assembled a set of
[tutorials](http://github.com/G-Node/python-odml/blob/master/doc/tutorial.rst "Python Tutorial").
[tutorials](https://python-odml.readthedocs.io/en/latest/tutorial.html "Python Tutorial").

## Python convenience scripts

Expand Down
24 changes: 22 additions & 2 deletions odml/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,29 @@
from .info import VERSION
from .tools.parser_utils import SUPPORTED_PARSERS as PARSERS

if _python_version.major < 3 or _python_version.major == 3 and _python_version.minor < 6:

def _format_warning(warn_msg, *args, **kwargs):
"""
Used to provide users with deprecation warnings via the warnings module
but without spamming them with full stack traces.
"""
final_msg = "%s\n" % str(warn_msg)
# If available add category name to the message
if args and hasattr(args[0], "__name__"):
final_msg = "%s: %s" % (args[0].__name__, final_msg)

return final_msg


# Monkey patch formatting 'warnings' messages for the whole module.
warnings.formatwarning = _format_warning

if _python_version.major < 3:
msg = "Python 2 has been deprecated.\n\todML support for Python 2 will be dropped August 2020."
warnings.warn(msg, category=DeprecationWarning, stacklevel=2)
elif _python_version.major == 3 and _python_version.minor < 6:
msg = "The '%s' package is not tested with your Python version. " % __name__
msg += "Please consider upgrading to the latest Python distribution."
msg += "\n\tPlease consider upgrading to the latest Python distribution."
warnings.warn(msg)

__version__ = VERSION
Expand Down
14 changes: 12 additions & 2 deletions odml/property.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
This module provides the Base Property class.
"""
import uuid
import warnings

from . import base
from . import dtypes
Expand All @@ -12,6 +13,10 @@
from .util import format_cardinality


MSG_VALUE_DEPRECATION = "The attribute 'value' is deprecated and will be removed, " \
"use 'values' instead."


def odml_tuple_import(t_count, new_value):
"""
Checks via a heuristic if the values in a string fit the general
Expand Down Expand Up @@ -131,6 +136,8 @@ def __init__(self, name=None, values=None, parent=None, unit=None,
self._values = []
self.values = values
if not values and (value or isinstance(value, (bool, int))):
# Using stacklevel=2 to avoid file name and code line in the message output.
warnings.warn(MSG_VALUE_DEPRECATION, category=DeprecationWarning, stacklevel=2)
self.values = value

self.parent = parent
Expand Down Expand Up @@ -285,7 +292,9 @@ def value(self):
"""
Deprecated alias of 'values'. Will be removed with the next minor release.
"""
print("The attribute 'value' is deprecated. Please use 'values' instead.")
# Using stacklevel=2 to avoid file name and code line in the message output.
warnings.warn(MSG_VALUE_DEPRECATION, category=DeprecationWarning, stacklevel=2)

return self.values

@value.setter
Expand All @@ -295,7 +304,8 @@ def value(self, new_value):

:param new_value: a single value or list of values.
"""
print("The attribute 'value' is deprecated. Please use 'values' instead.")
# Using stacklevel=2 to avoid file name and code line in the message output.
warnings.warn(MSG_VALUE_DEPRECATION, category=DeprecationWarning, stacklevel=2)
self.values = new_value

def value_str(self, index=0):
Expand Down
20 changes: 16 additions & 4 deletions odml/section.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
This module provides the Base Section class.
"""
import uuid
import warnings

try:
from collections.abc import Iterable
Expand Down Expand Up @@ -774,21 +775,32 @@ def reorder(self, new_index):

return self._reorder(self.parent.sections, new_index)

def create_property(self, name, value=None, dtype=None, oid=None):
def create_property(self, name, values=None, dtype=None, oid=None, value=None):
"""
Create a new property that is a child of this section.

:param name: The name of the property.
:param value: Some data value, it can be a single value or
a list of homogeneous values.
:param values: Some data value, it can be a single value or
a list of homogeneous values.
:param dtype: The data type of the values stored in the property,
if dtype is not given, the type is deduced from the values.
Check odml.DType for supported data types.
:param oid: object id, UUID string as specified in RFC 4122. If no id
is provided, an id will be generated and assigned.
:param value: Deprecated alias of 'values'. Any content of 'value' is ignored,
if 'values' is set.

:return: The new property.
"""
prop = BaseProperty(name=name, value=value, dtype=dtype, oid=oid)
if value and values:
print("Warning: Both 'values' and 'value' were set; ignoring 'value'.")

if not values and (value or isinstance(value, (bool, int))):
msg = "The attribute 'value' is deprecated and will be removed, use 'values' instead."
warnings.warn(msg, category=DeprecationWarning, stacklevel=2)
values = value

prop = BaseProperty(name=name, values=values, dtype=dtype, oid=oid)
prop.parent = self

return prop
Expand Down
3 changes: 2 additions & 1 deletion odml/tools/converters/version_converter.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
from lxml import etree as ET

from ..parser_utils import ParserException
from ..xmlparser import XML_HEADER

from ...format import Document, Section, Property
from ...info import FORMAT_VERSION
Expand Down Expand Up @@ -531,5 +532,5 @@ def write_to_file(self, filename, backend="XML"):

if data and "<odML " in data:
with open(filename, "w") as file:
file.write('<?xml version="1.0" encoding="UTF-8"?>\n')
file.write("%s\n" % XML_HEADER)
file.write(data)
8 changes: 5 additions & 3 deletions odml/tools/version_converter.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,11 @@
This module provides backwards compatibility for the VersionConverter class.
It is deprecated and will be removed in future versions.
"""
import warnings

from .converters import VersionConverter

print("[DEPRECATION WARNING] The VersionConverter file has been moved to "
"'odml.tools.converters' and will be removed from 'odml.tools' in future "
"odML releases. Please update the imports in your code accordingly.")
_MSG = "The VersionConverter file has been moved to "\
"'odml.tools.converters' and will be removed from 'odml.tools' in future "\
"odML releases. Please update the imports in your code accordingly."
warnings.warn(_MSG, category=DeprecationWarning, stacklevel=2)
19 changes: 16 additions & 3 deletions test/test_doc.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
import os
import unittest

from glob import glob

try:
from urllib.request import pathname2url
except ImportError:
Expand All @@ -10,11 +12,23 @@
from odml import Document, Section, Property
from odml.doc import BaseDocument
from odml.dtypes import FORMAT_DATE
from .util import ODML_CACHE_DIR as CACHE_DIR, TEST_RESOURCES_DIR as RES_DIR


class TestSection(unittest.TestCase):
def setUp(self):
pass
self.local_repo_file = "local_repository_file_v1.1.xml"

def tearDown(self):
"""
Remove all files loaded to the terminology cache directory
to avoid test cross pollution.
"""
temp_file_glob = "*%s" % self.local_repo_file
find_us = os.path.join(CACHE_DIR, temp_file_glob)

for file_path in glob(find_us):
os.remove(file_path)

def test_simple_attributes(self):
author = "HPL"
Expand Down Expand Up @@ -95,8 +109,7 @@ def test_date(self):
doc.date = "some format"

def test_get_terminology_equivalent(self):
dir_path = os.path.dirname(os.path.realpath(__file__))
repo_file = os.path.join(dir_path, "resources", "local_repository_file_v1.1.xml")
repo_file = os.path.join(RES_DIR, self.local_repo_file)
local_url = "file://%s" % pathname2url(repo_file)

doc = Document(repository=local_url)
Expand Down
6 changes: 3 additions & 3 deletions test/test_doc_integration.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,17 +5,17 @@

import os
import shutil
import tempfile
import unittest

import odml
from .util import create_test_dir


class TestDocumentIntegration(unittest.TestCase):

def setUp(self):
# Set up test environment
self.tmp_dir = tempfile.mkdtemp(suffix=".odml")
self.tmp_dir = create_test_dir(__file__)

self.json_file = os.path.join(self.tmp_dir, "test.json")
self.xml_file = os.path.join(self.tmp_dir, "test.xml")
Expand All @@ -26,7 +26,7 @@ def setUp(self):
self.doc = doc

def tearDown(self):
if os.path.exists(self.tmp_dir):
if self.tmp_dir and os.path.exists(self.tmp_dir):
shutil.rmtree(self.tmp_dir)

def save_load(self):
Expand Down
6 changes: 3 additions & 3 deletions test/test_dtypes_integration.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,17 +7,17 @@
import datetime as dt
import os
import shutil
import tempfile
import unittest

import odml
from .util import create_test_dir


class TestTypesIntegration(unittest.TestCase):

def setUp(self):
# Set up test environment
self.tmp_dir = tempfile.mkdtemp(suffix=".odml")
self.tmp_dir = create_test_dir(__file__)

self.json_file = os.path.join(self.tmp_dir, "test.json")
self.xml_file = os.path.join(self.tmp_dir, "test.xml")
Expand All @@ -29,7 +29,7 @@ def setUp(self):
self.doc = doc

def tearDown(self):
if os.path.exists(self.tmp_dir):
if self.tmp_dir and os.path.exists(self.tmp_dir):
shutil.rmtree(self.tmp_dir)

def test_time(self):
Expand Down
24 changes: 14 additions & 10 deletions test/test_fileio.py
Original file line number Diff line number Diff line change
@@ -1,40 +1,44 @@
import unittest
import sys
import os
import odml
import sys
import unittest

try:
from StringIO import StringIO
except ImportError:
from io import StringIO

import odml

from .util import TEST_RESOURCES_DIR as RES_DIR


class TestTypes(unittest.TestCase):

def setUp(self):
self.dir_path = os.path.dirname(os.path.realpath(__file__))
self.file = os.path.join(self.dir_path, 'resources', 'example.odml')
self.file = os.path.join(RES_DIR, "example.odml")
# Do not allow anything to be printed on STDOUT
self.captured_stdout = StringIO()
sys.stdout = self.captured_stdout

def test_load_save(self):
doc = odml.load(self.file)
self.assertTrue(isinstance(doc, odml.doc.BaseDocument))
odml.save(doc, self.file + '_copy')
os.remove(self.file + '_copy')
file_name = "%s_copy" % self.file
odml.save(doc, file_name)
os.remove(file_name)

def test_display(self):
doc = odml.load(self.file)
odml.display(doc)

def test_invalid_parser(self):
with self.assertRaises(NotImplementedError):
odml.load(self.file, 'html')
odml.load(self.file, "html")

doc = odml.load(self.file)
with self.assertRaises(NotImplementedError):
odml.save(doc, self.file + '_copy_html', 'html')
file_name = "%s_copy_html" % self.file
odml.save(doc, file_name, "html")

with self.assertRaises(NotImplementedError):
odml.display(doc, 'html')
odml.display(doc, "html")
Loading