Skip to content

Commit 66e11c9

Browse files
authored
Merge pull request from molshape/pytest
Update v1.0.0
2 parents 8216e15 + b24dbd2 commit 66e11c9

4 files changed

Lines changed: 169 additions & 19 deletions

File tree

.github/workflows/python-app.yml

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
# This workflow will install Python dependencies, run tests and lint with a single version of Python
2+
# For more information see: https://help.github.com/actions/language-and-framework-guides/using-python-with-github-actions
3+
4+
name: Python application
5+
6+
on:
7+
push:
8+
branches: [ pytest ]
9+
pull_request:
10+
branches: [ pytest ]
11+
12+
jobs:
13+
build:
14+
15+
runs-on: ubuntu-latest
16+
17+
steps:
18+
- uses: actions/checkout@v2
19+
- name: Set up Python 3.8
20+
uses: actions/setup-python@v2
21+
with:
22+
python-version: 3.8
23+
- name: Install dependencies
24+
run: |
25+
python -m pip install --upgrade pip
26+
pip install flake8 pytest
27+
if [ -f requirements.txt ]; then pip install -r requirements.txt; fi
28+
pip install casregnum
29+
- name: Lint with flake8
30+
run: |
31+
# stop the build if there are Python syntax errors or undefined names
32+
flake8 . --count --select=E9,F63,F7,F82 --show-source --statistics
33+
# exit-zero treats all errors as warnings. The GitHub editor is 127 chars wide
34+
flake8 . --count --exit-zero --max-complexity=10 --max-line-length=127 --statistics
35+
- name: Test with pytest
36+
run: |
37+
pytest

src/casregnum.py

Lines changed: 20 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,8 @@
33
"""
44
Class for CAS Registry Numbers® (CAS RN®)
55
allwos to manage, check and sort CAS Registry Numbers®
6-
see https://www.cas.org/support/documentation/chemical-substances/checkdig for a complete specification of the CAS Registry Numbers®
6+
see https://www.cas.org/support/documentation/chemical-substances/checkdig
7+
for a complete specification of the CAS Registry Numbers®
78
and the calculation method to determine the check digit
89
"""
910

@@ -15,47 +16,48 @@ def __init__(self, cas_rn):
1516
self.cas_integer = cas_rn
1617
# convert integer into (formatted) CAS string
1718
self.cas_string = re.sub(
18-
"^(\d{2,7})(\d{2})(\d{1})$", "\g<1>-\g<2>-\g<3>", str(cas_rn)
19+
r"^(\d{2,7})(\d{2})(\d{1})$", r"\g<1>-\g<2>-\g<3>", str(cas_rn)
1920
)
2021
# case that cas_rn is a string
2122
elif isinstance(cas_rn, str):
2223
self.cas_string = cas_rn
2324
# case that cas_rn is neither an integer nor a string
2425
else:
2526
raise TypeError(
26-
f"Invalid CAS Registry Number format '{cas_rn}' (expected an integer (<class 'int'>) or a string (<class 'str'>), but found {type(cas_rn)})"
27+
f"Invalid CAS Registry Number format '{cas_rn}' (expected an integer (<class 'int'>) "
28+
f"or a string (<class 'str'>), but found {type(cas_rn)})"
2729
)
2830
# extract check digit = last digit of the CAS number
2931
self.check_digit = int(str(cas_rn)[-1])
3032

31-
### default string output for CAS Registry Numbers
33+
# default string output for CAS Registry Numbers
3234
def __str__(self):
3335
return str(self.cas_string)
3436

35-
### defines a string format for CAS Registry Numbers
37+
# defines a string format for CAS Registry Numbers
3638
def __format__(self, format_spec):
3739
return f"{self.cas_string:{format_spec}}"
3840

39-
### checks if two CAS Registry Numbers are equal
41+
# checks if two CAS Registry Numbers are equal
4042
def __eq__(self, other):
4143
return True if self.cas_integer == other.cas_integer else False
4244

43-
### checks if self.cas_integer < other.cas_integer
45+
# checks if self.cas_integer < other.cas_integer
4446
def __lt__(self, other):
4547
return True if self.cas_integer < other.cas_integer else False
4648

47-
### Returns CAS Registry Number
49+
# Returns CAS Registry Number
4850
@property
4951
def cas_string(self):
5052
return self.__cas_string
5153

52-
### Sets CAS Registry Number
53-
### if the passed input value is a string, parse the string according to _____00-00-0
54-
### if the passed input value is an integer, create the string arrocing to _____00-00-0
54+
# Sets CAS Registry Number
55+
# if the passed input value is a string, parse the string according to _____00-00-0
56+
# if the passed input value is an integer, create the string arrocing to _____00-00-0
5557
@cas_string.setter
5658
def cas_string(self, cas_rn):
5759
# convert (formatted) CAS string into integer
58-
if regex_cas := re.match("^(\d{2,7})\-(\d{2})-(\d{1})$", cas_rn):
60+
if regex_cas := re.match(r"^(\d{2,7})\-(\d{2})-(\d{1})$", cas_rn):
5961
self.cas_integer = self.__cas_integer = int(
6062
regex_cas.group(1) + regex_cas.group(2) + regex_cas.group(3)
6163
)
@@ -66,7 +68,7 @@ def cas_string(self, cas_rn):
6668
)
6769
self.__cas_string = cas_rn
6870

69-
### Returns CAS Registry Number as an integer (without the hyphens)
71+
# Returns CAS Registry Number as an integer (without the hyphens)
7072
@property
7173
def cas_integer(self):
7274
return self.__cas_integer
@@ -77,17 +79,17 @@ def cas_integer(self, cas_rn):
7779
# the officially lowest CAS number on record is 35-66-5 (as of June 2019)
7880
# (Source: https://twitter.com/CASChemistry/status/1144222698740092929)
7981
if cas_rn < 10004 or cas_rn > 9999999995:
80-
raise TypeError(
82+
raise ValueError(
8183
f"Invalid CAS number '{cas_rn}' (must be an integer between 10004 and 9999999995)"
8284
)
8385
self.__cas_integer = cas_rn
8486

85-
### Returns check digit of the CAS Registry Number
87+
# Returns check digit of the CAS Registry Number
8688
@property
8789
def check_digit(self):
8890
return self.__check_digit
8991

90-
### Sets the CAS Registry Number check digit
92+
# Sets the CAS Registry Number check digit
9193
@check_digit.setter
9294
def check_digit(self, digit_to_test):
9395
# check if the check digit fits to the CAS Number
@@ -102,6 +104,7 @@ def check_digit(self, digit_to_test):
102104
# test (check sum mod 10) against check digit
103105
if check_sum % 10 != int(digit_to_test):
104106
raise ValueError(
105-
f"Invalid CAS number '{self.cas_string}' (found check digit '{digit_to_test}', but expected '{check_sum % 10}')"
107+
f"Invalid CAS number '{self.cas_string}' "
108+
f"(found check digit '{digit_to_test}', but expected '{check_sum % 10}')"
106109
)
107110
self.__check_digit = int(digit_to_test)

test/test.py renamed to test/example.py

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,8 @@
55
l_lacticacid = CAS(79_33_4)
66
d_lacticacid = CAS(10326_41_7)
77
octanes = [
8-
CAS(111_65_9), CAS(592_27_8), CAS(589_81_1), CAS(589_53_7), CAS( 590_73_8), CAS(584_94_1),
9-
CAS(589_43_5), CAS(592_13_2), CAS(563_16_6), CAS(583_48_2), CAS( 619_99_8), CAS(564_02_3),
8+
CAS(111_65_9), CAS(592_27_8), CAS(589_81_1), CAS(589_53_7), CAS(590_73_8), CAS(584_94_1),
9+
CAS(589_43_5), CAS(592_13_2), CAS(563_16_6), CAS(583_48_2), CAS(619_99_8), CAS(564_02_3),
1010
CAS(540_84_1), CAS(560_21_4), CAS(565_75_3), CAS(609_26_7), CAS(1067_08_9), CAS(594_82_1),
1111
]
1212

@@ -22,3 +22,16 @@
2222
print(f"{isomer:>9}", end=", ")
2323
if i % 6 == 0:
2424
print()
25+
26+
27+
# Output:
28+
# str: 58-08-2
29+
# int: 58082
30+
# check digit: 2
31+
# 58-08-2 == 58-08-2: True
32+
# 58-08-2 > 58-08-2: False
33+
# 79-33-4 > 10326-41-7: False
34+
# 79-33-4 < 10326-41-7: True
35+
# 111-65-9, 540-84-1, 560-21-4, 563-16-6, 564-02-3, 565-75-3,
36+
# 583-48-2, 584-94-1, 589-43-5, 589-53-7, 589-81-1, 590-73-8,
37+
# 592-13-2, 592-27-8, 594-82-1, 609-26-7, 619-99-8, 1067-08-9,

test/test_functionality.py

Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
import pytest
2+
from casregnum import CAS
3+
4+
5+
@pytest.fixture
6+
def octanes():
7+
return [
8+
CAS(111_65_9), CAS(592_27_8), CAS(589_81_1), CAS(589_53_7), CAS(590_73_8), CAS(584_94_1),
9+
CAS(589_43_5), CAS(592_13_2), CAS(563_16_6), CAS(583_48_2), CAS(619_99_8), CAS(564_02_3),
10+
CAS(540_84_1), CAS(560_21_4), CAS(565_75_3), CAS(609_26_7), CAS(1067_08_9), CAS(594_82_1),
11+
]
12+
13+
14+
@pytest.fixture
15+
def octanes_sorted():
16+
return [
17+
CAS(111_65_9), CAS(540_84_1), CAS(560_21_4), CAS(563_16_6), CAS(564_02_3), CAS(565_75_3),
18+
CAS(583_48_2), CAS(584_94_1), CAS(589_43_5), CAS(589_53_7), CAS(589_81_1), CAS(590_73_8),
19+
CAS(592_13_2), CAS(592_27_8), CAS(594_82_1), CAS(609_26_7), CAS(619_99_8), CAS(1067_08_9),
20+
]
21+
22+
23+
# pytest fixtures
24+
25+
26+
@pytest.fixture
27+
def caffeine():
28+
return CAS(58_08_2)
29+
30+
31+
@pytest.fixture
32+
def theine():
33+
return CAS("58-08-2")
34+
35+
36+
@pytest.fixture
37+
def l_lacticacid():
38+
return CAS(79_33_4)
39+
40+
41+
@pytest.fixture
42+
def d_lacticacid():
43+
return CAS("10326-41-7")
44+
45+
46+
# Tests for functionality
47+
48+
49+
def test_cas_check_digit(caffeine):
50+
assert caffeine.check_digit == 2
51+
52+
53+
def test_cas_check_digit_print(caffeine):
54+
assert caffeine.check_digit
55+
56+
57+
def test_cas_check_digit(caffeine):
58+
assert str(caffeine) == "58-08-2"
59+
60+
61+
def test_cas_equal(caffeine, theine):
62+
assert caffeine == theine
63+
64+
65+
def test_cas_lesser_than(l_lacticacid, d_lacticacid):
66+
assert l_lacticacid < d_lacticacid
67+
68+
69+
def test_cas_format_string(caffeine):
70+
assert f"{caffeine:0>12}"
71+
72+
73+
def test_for_sorting(octanes, octanes_sorted):
74+
assert sorted(octanes) == octanes_sorted
75+
76+
77+
# Tests for error handling
78+
79+
80+
@pytest.mark.xfail(raises=TypeError)
81+
def test_cas_invalid_input():
82+
CAS(6417.5)
83+
84+
85+
@pytest.mark.xfail(raises=ValueError)
86+
def test_cas_format_unreadable():
87+
CAS("64 - 17 - 5")
88+
89+
90+
@pytest.mark.xfail(raises=ValueError)
91+
def test_cas_range_error():
92+
CAS(100)
93+
94+
95+
@pytest.mark.xfail(raises=ValueError)
96+
def test_cas_check_digit_error():
97+
CAS("64-17-6")

0 commit comments

Comments
 (0)