Skip to content

Commit 266f1c1

Browse files
authored
Merge pull request #388 from mpsonntag/smallFixes
Minor issue fixes and test reorganisation LGTM
2 parents 1083db4 + c6e13cf commit 266f1c1

25 files changed

+233
-147
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ pip install odml
3636
## Tutorial and examples
3737

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

4141
## Python convenience scripts
4242

odml/__init__.py

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,29 @@
1212
from .info import VERSION
1313
from .tools.parser_utils import SUPPORTED_PARSERS as PARSERS
1414

15-
if _python_version.major < 3 or _python_version.major == 3 and _python_version.minor < 6:
15+
16+
def _format_warning(warn_msg, *args, **kwargs):
17+
"""
18+
Used to provide users with deprecation warnings via the warnings module
19+
but without spamming them with full stack traces.
20+
"""
21+
final_msg = "%s\n" % str(warn_msg)
22+
# If available add category name to the message
23+
if args and hasattr(args[0], "__name__"):
24+
final_msg = "%s: %s" % (args[0].__name__, final_msg)
25+
26+
return final_msg
27+
28+
29+
# Monkey patch formatting 'warnings' messages for the whole module.
30+
warnings.formatwarning = _format_warning
31+
32+
if _python_version.major < 3:
33+
msg = "Python 2 has been deprecated.\n\todML support for Python 2 will be dropped August 2020."
34+
warnings.warn(msg, category=DeprecationWarning, stacklevel=2)
35+
elif _python_version.major == 3 and _python_version.minor < 6:
1636
msg = "The '%s' package is not tested with your Python version. " % __name__
17-
msg += "Please consider upgrading to the latest Python distribution."
37+
msg += "\n\tPlease consider upgrading to the latest Python distribution."
1838
warnings.warn(msg)
1939

2040
__version__ = VERSION

odml/property.py

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
This module provides the Base Property class.
44
"""
55
import uuid
6+
import warnings
67

78
from . import base
89
from . import dtypes
@@ -12,6 +13,10 @@
1213
from .util import format_cardinality
1314

1415

16+
MSG_VALUE_DEPRECATION = "The attribute 'value' is deprecated and will be removed, " \
17+
"use 'values' instead."
18+
19+
1520
def odml_tuple_import(t_count, new_value):
1621
"""
1722
Checks via a heuristic if the values in a string fit the general
@@ -131,6 +136,8 @@ def __init__(self, name=None, values=None, parent=None, unit=None,
131136
self._values = []
132137
self.values = values
133138
if not values and (value or isinstance(value, (bool, int))):
139+
# Using stacklevel=2 to avoid file name and code line in the message output.
140+
warnings.warn(MSG_VALUE_DEPRECATION, category=DeprecationWarning, stacklevel=2)
134141
self.values = value
135142

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

291300
@value.setter
@@ -295,7 +304,8 @@ def value(self, new_value):
295304
296305
:param new_value: a single value or list of values.
297306
"""
298-
print("The attribute 'value' is deprecated. Please use 'values' instead.")
307+
# Using stacklevel=2 to avoid file name and code line in the message output.
308+
warnings.warn(MSG_VALUE_DEPRECATION, category=DeprecationWarning, stacklevel=2)
299309
self.values = new_value
300310

301311
def value_str(self, index=0):

odml/section.py

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
This module provides the Base Section class.
44
"""
55
import uuid
6+
import warnings
67

78
try:
89
from collections.abc import Iterable
@@ -774,21 +775,32 @@ def reorder(self, new_index):
774775

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

777-
def create_property(self, name, value=None, dtype=None, oid=None):
778+
def create_property(self, name, values=None, dtype=None, oid=None, value=None):
778779
"""
779780
Create a new property that is a child of this section.
780781
781782
:param name: The name of the property.
782-
:param value: Some data value, it can be a single value or
783-
a list of homogeneous values.
783+
:param values: Some data value, it can be a single value or
784+
a list of homogeneous values.
784785
:param dtype: The data type of the values stored in the property,
785786
if dtype is not given, the type is deduced from the values.
786787
Check odml.DType for supported data types.
787788
:param oid: object id, UUID string as specified in RFC 4122. If no id
788789
is provided, an id will be generated and assigned.
790+
:param value: Deprecated alias of 'values'. Any content of 'value' is ignored,
791+
if 'values' is set.
792+
789793
:return: The new property.
790794
"""
791-
prop = BaseProperty(name=name, value=value, dtype=dtype, oid=oid)
795+
if value and values:
796+
print("Warning: Both 'values' and 'value' were set; ignoring 'value'.")
797+
798+
if not values and (value or isinstance(value, (bool, int))):
799+
msg = "The attribute 'value' is deprecated and will be removed, use 'values' instead."
800+
warnings.warn(msg, category=DeprecationWarning, stacklevel=2)
801+
values = value
802+
803+
prop = BaseProperty(name=name, values=values, dtype=dtype, oid=oid)
792804
prop.parent = self
793805

794806
return prop

odml/tools/converters/version_converter.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
from lxml import etree as ET
1414

1515
from ..parser_utils import ParserException
16+
from ..xmlparser import XML_HEADER
1617

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

532533
if data and "<odML " in data:
533534
with open(filename, "w") as file:
534-
file.write('<?xml version="1.0" encoding="UTF-8"?>\n')
535+
file.write("%s\n" % XML_HEADER)
535536
file.write(data)

odml/tools/version_converter.py

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,11 @@
22
This module provides backwards compatibility for the VersionConverter class.
33
It is deprecated and will be removed in future versions.
44
"""
5+
import warnings
56

67
from .converters import VersionConverter
78

8-
print("[DEPRECATION WARNING] The VersionConverter file has been moved to "
9-
"'odml.tools.converters' and will be removed from 'odml.tools' in future "
10-
"odML releases. Please update the imports in your code accordingly.")
9+
_MSG = "The VersionConverter file has been moved to "\
10+
"'odml.tools.converters' and will be removed from 'odml.tools' in future "\
11+
"odML releases. Please update the imports in your code accordingly."
12+
warnings.warn(_MSG, category=DeprecationWarning, stacklevel=2)

test/test_doc.py

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22
import os
33
import unittest
44

5+
from glob import glob
6+
57
try:
68
from urllib.request import pathname2url
79
except ImportError:
@@ -10,11 +12,23 @@
1012
from odml import Document, Section, Property
1113
from odml.doc import BaseDocument
1214
from odml.dtypes import FORMAT_DATE
15+
from .util import ODML_CACHE_DIR as CACHE_DIR, TEST_RESOURCES_DIR as RES_DIR
1316

1417

1518
class TestSection(unittest.TestCase):
1619
def setUp(self):
17-
pass
20+
self.local_repo_file = "local_repository_file_v1.1.xml"
21+
22+
def tearDown(self):
23+
"""
24+
Remove all files loaded to the terminology cache directory
25+
to avoid test cross pollution.
26+
"""
27+
temp_file_glob = "*%s" % self.local_repo_file
28+
find_us = os.path.join(CACHE_DIR, temp_file_glob)
29+
30+
for file_path in glob(find_us):
31+
os.remove(file_path)
1832

1933
def test_simple_attributes(self):
2034
author = "HPL"
@@ -95,8 +109,7 @@ def test_date(self):
95109
doc.date = "some format"
96110

97111
def test_get_terminology_equivalent(self):
98-
dir_path = os.path.dirname(os.path.realpath(__file__))
99-
repo_file = os.path.join(dir_path, "resources", "local_repository_file_v1.1.xml")
112+
repo_file = os.path.join(RES_DIR, self.local_repo_file)
100113
local_url = "file://%s" % pathname2url(repo_file)
101114

102115
doc = Document(repository=local_url)

test/test_doc_integration.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,17 +5,17 @@
55

66
import os
77
import shutil
8-
import tempfile
98
import unittest
109

1110
import odml
11+
from .util import create_test_dir
1212

1313

1414
class TestDocumentIntegration(unittest.TestCase):
1515

1616
def setUp(self):
1717
# Set up test environment
18-
self.tmp_dir = tempfile.mkdtemp(suffix=".odml")
18+
self.tmp_dir = create_test_dir(__file__)
1919

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

2828
def tearDown(self):
29-
if os.path.exists(self.tmp_dir):
29+
if self.tmp_dir and os.path.exists(self.tmp_dir):
3030
shutil.rmtree(self.tmp_dir)
3131

3232
def save_load(self):

test/test_dtypes_integration.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,17 +7,17 @@
77
import datetime as dt
88
import os
99
import shutil
10-
import tempfile
1110
import unittest
1211

1312
import odml
13+
from .util import create_test_dir
1414

1515

1616
class TestTypesIntegration(unittest.TestCase):
1717

1818
def setUp(self):
1919
# Set up test environment
20-
self.tmp_dir = tempfile.mkdtemp(suffix=".odml")
20+
self.tmp_dir = create_test_dir(__file__)
2121

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

3131
def tearDown(self):
32-
if os.path.exists(self.tmp_dir):
32+
if self.tmp_dir and os.path.exists(self.tmp_dir):
3333
shutil.rmtree(self.tmp_dir)
3434

3535
def test_time(self):

test/test_fileio.py

Lines changed: 14 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,40 +1,44 @@
1-
import unittest
2-
import sys
31
import os
4-
import odml
2+
import sys
3+
import unittest
54

65
try:
76
from StringIO import StringIO
87
except ImportError:
98
from io import StringIO
109

10+
import odml
11+
12+
from .util import TEST_RESOURCES_DIR as RES_DIR
13+
1114

1215
class TestTypes(unittest.TestCase):
1316

1417
def setUp(self):
15-
self.dir_path = os.path.dirname(os.path.realpath(__file__))
16-
self.file = os.path.join(self.dir_path, 'resources', 'example.odml')
18+
self.file = os.path.join(RES_DIR, "example.odml")
1719
# Do not allow anything to be printed on STDOUT
1820
self.captured_stdout = StringIO()
1921
sys.stdout = self.captured_stdout
2022

2123
def test_load_save(self):
2224
doc = odml.load(self.file)
2325
self.assertTrue(isinstance(doc, odml.doc.BaseDocument))
24-
odml.save(doc, self.file + '_copy')
25-
os.remove(self.file + '_copy')
26+
file_name = "%s_copy" % self.file
27+
odml.save(doc, file_name)
28+
os.remove(file_name)
2629

2730
def test_display(self):
2831
doc = odml.load(self.file)
2932
odml.display(doc)
3033

3134
def test_invalid_parser(self):
3235
with self.assertRaises(NotImplementedError):
33-
odml.load(self.file, 'html')
36+
odml.load(self.file, "html")
3437

3538
doc = odml.load(self.file)
3639
with self.assertRaises(NotImplementedError):
37-
odml.save(doc, self.file + '_copy_html', 'html')
40+
file_name = "%s_copy_html" % self.file
41+
odml.save(doc, file_name, "html")
3842

3943
with self.assertRaises(NotImplementedError):
40-
odml.display(doc, 'html')
44+
odml.display(doc, "html")

0 commit comments

Comments
 (0)