Skip to content

Commit b9b813f

Browse files
author
Max
committed
refactor. Add more validation in market_scraper, update validation tests, update README, fix lint.yml.
- Add more validation of the parameters of the market_scraper function. - Update the validation tests for the market_scraper function parameters. - Update and add some validation tests for the market_scraper function parameters. - Update the README - Fix lint.yml
1 parent 274c9a3 commit b9b813f

4 files changed

Lines changed: 166 additions & 46 deletions

File tree

.github/workflows/lint.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ jobs:
5959

6060
- name: Run Isort
6161
run: |
62-
isort . --verbose
62+
isort . --check --verbose
6363
6464
flake8:
6565
runs-on: ubuntu-latest

README.md

Lines changed: 21 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,26 @@
11
# Steam Market Scraper Parser
2-
<p>
3-
<a style="text-decoration: none; color: inherit;"
4-
href='https://github.com/RandomProgramm3r/Steam-Market-Scraper/actions/workflows/lint.yml/'>
5-
<img
6-
src='https://github.com/RandomProgramm3r/Steam-Market-Scraper/actions/workflows/lint.yml/badge.svg'
7-
alt='pipeline status'
8-
height='30'
9-
width='140'
10-
>
11-
</a>
12-
<a
13-
href='https://steamcommunity.com/market/'>
14-
<img
15-
src='https://steamcommunity.com/favicon.ico'
16-
alt='Steam logo'
17-
height='30'
18-
width='30'>
19-
</a>
20-
</p>
212

3+
<div
4+
style="display: flex;
5+
align-items: center;">
6+
<a
7+
style="text-decoration: none; color: inherit; margin-right:10px;"
8+
href="https://github.com/RandomProgramm3r/Steam-Market-Scraper/actions/workflows/lint.yml/">
9+
<img
10+
src="https://github.com/RandomProgramm3r/Steam-Market-Scraper/actions/workflows/lint.yml/badge.svg"
11+
alt="pipeline status"
12+
height="30"
13+
width="140">
14+
</a>
15+
<a
16+
href="https://steamcommunity.com/market/">
17+
<img
18+
src="https://steamcommunity.com/favicon.ico"
19+
alt="Steam logo"
20+
height="30"
21+
width="30">
22+
</a>
23+
</div>
2224

2325
## TOC
2426

scraper.py

Lines changed: 84 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,19 @@ def validate_enum_value(
1212
enum_class: enum.Enum,
1313
field_name: str,
1414
) -> None:
15+
"""
16+
Validates that the provided value is an integer and exists
17+
within the given enum.
18+
19+
Args:
20+
value (int): The value to validate.
21+
enum_class (enum.Enum): The enumeration to check against.
22+
field_name (str): The name of the field (used in error messages).
23+
24+
Raises:
25+
TypeError: If the value is not an integer.
26+
ValueError: If the value is not found in the provided enumeration.
27+
"""
1528
if not isinstance(value, int):
1629
raise TypeError(
1730
(
@@ -23,23 +36,89 @@ def validate_enum_value(
2336
if value not in (item.value for item in enum_class):
2437
raise ValueError(
2538
(
26-
f'Invalid {field_name} ({value}): not found in '
27-
f'{enum_class.__name__}.'
39+
f'Invalid {field_name} ({value}): '
40+
f'not found in {enum_class.__name__}.'
41+
),
42+
)
43+
44+
45+
def validate_parameters(item_name: str, app_id: int, currency: int) -> None:
46+
"""
47+
Validates the input parameters for the market_scraper function.
48+
49+
It checks that:
50+
- item_name is a string.
51+
- item_name isn't consist solely of whitespace.
52+
- app_id and currency are valid according to their
53+
respective enums in data.py.
54+
55+
Args:
56+
item_name (str): The full name of the item.
57+
app_id (int): The application ID to be validated against data.Apps.
58+
currency (int): The currency code to be validated against
59+
data.Currency.
60+
61+
Raises:
62+
ValueError: If item_name is consist solely of whitespace
63+
or if app_id/currency are invalid.
64+
TypeError: If app_id or currency are not integers
65+
or if item_name isn't str.
66+
"""
67+
if not isinstance(item_name, str):
68+
raise TypeError(
69+
(
70+
f'Invalid type for item_name: expected str, '
71+
f'got {type(item_name).__name__}.'
2872
),
2973
)
3074

75+
if not item_name.strip():
76+
raise ValueError('Item name cannot consist solely of whitespace.')
77+
78+
validate_enum_value(app_id, data.Apps, 'app ID')
79+
validate_enum_value(currency, data.Currency, 'currency')
80+
3181

3282
def encode_item_name(item_name: str) -> str:
33-
return urllib.parse.quote_plus(item_name).replace('~', '%7E')
83+
"""
84+
Encodes the item name for use in a URL.
85+
86+
The function first removes any leading and trailing whitespace
87+
from the item name, then applies URL encoding using quote_plus, and finally
88+
replaces the '~' characterwith '%7E' to ensure proper encoding.
89+
90+
Args:
91+
item_name (str): The name of the item.
92+
Whitespace at the beginning and end is removed.
93+
94+
Returns:
95+
str: The URL-encoded item name.
96+
"""
97+
return urllib.parse.quote_plus(item_name.strip()).replace('~', '%7E')
3498

3599

36100
def market_scraper(
37101
item_name: str,
38102
app_id: int,
39103
currency: int = data.Currency.USD.value,
40104
) -> str:
41-
validate_enum_value(app_id, data.Apps, 'app ID')
42-
validate_enum_value(currency, data.Currency, 'currency')
105+
"""
106+
Retrieves data from the Steam Community Market for the specified item.
107+
108+
The function validates the input parameters, encodes the item name,
109+
constructs the URL, and performs an HTTP request. It returns a formatted
110+
JSON response or an error message.
111+
112+
Args:
113+
item_name (str): The full name of the item.
114+
app_id (int): The application ID for the request.
115+
currency (int, optional): The currency code.
116+
Defaults to data.Currency.USD.value. (USD)
117+
118+
Returns:
119+
str: A formatted JSON response or an error message.
120+
"""
121+
validate_parameters(item_name, app_id, currency)
43122

44123
encoded_name = encode_item_name(item_name)
45124

test_market_scraper.py

Lines changed: 60 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -7,53 +7,92 @@
77

88
class TestEncodeItemName(unittest.TestCase):
99
def test_encode_item_name_regular(self):
10-
item = 'Dreams & Nightmares'
10+
item_name = 'Dreams & Nightmares'
1111
expected = 'Dreams+%26+Nightmares'
12-
self.assertEqual(scraper.encode_item_name(item), expected)
12+
self.assertEqual(scraper.encode_item_name(item_name), expected)
1313

1414
def test_encode_item_name_tilde(self):
15-
item = 'Music Kit | Chipzel, ~Yellow Magic~'
15+
item_name = 'Music Kit | Chipzel, ~Yellow Magic~'
1616
expected = 'Music+Kit+%7C+Chipzel%2C+%7EYellow+Magic%7E'
17-
self.assertEqual(scraper.encode_item_name(item), expected)
17+
self.assertEqual(scraper.encode_item_name(item_name), expected)
18+
19+
def test_encode_item_with_leading_and_trailing_whitespaces(self):
20+
item_name = ' AWP | Neo-Noir (Factory New) '
21+
expected = 'AWP+%7C+Neo-Noir+%28Factory+New%29'
22+
self.assertEqual(scraper.encode_item_name(item_name), expected)
1823

1924
def test_encode_item_name_empty(self):
2025
self.assertEqual(scraper.encode_item_name(''), '')
2126

2227
def test_encode_item_name_none(self):
23-
with self.assertRaises(TypeError):
28+
with self.assertRaises(AttributeError):
2429
scraper.encode_item_name(None)
2530

2631

27-
class ValidateEnumValue(unittest.TestCase):
28-
def test_market_scraper_invalid_app_type(self):
32+
class ValidateParameters(unittest.TestCase):
33+
def test_market_scraper_invalid_item_name_type(self):
34+
with self.assertRaises(TypeError):
35+
scraper.market_scraper(
36+
11111,
37+
data.Apps.CS2.value,
38+
data.Currency.USD.value,
39+
)
40+
41+
def test_market_scraper_invalid_app_id_type(self):
2942
with self.assertRaises(TypeError):
30-
app_id = 'cs2'
31-
scraper.validate_enum_value(app_id, data.Apps, 'app ID')
43+
scraper.validate_parameters(
44+
'Dreams & Nightmares Case',
45+
'CS2',
46+
data.Currency.USD.value,
47+
)
3248

3349
def test_market_scraper_invalid_currency_type(self):
3450
with self.assertRaises(TypeError):
35-
currency = 'usd'
36-
scraper.validate_enum_value(currency, data.Currency, 'currency')
51+
scraper.validate_parameters(
52+
'Dreams & Nightmares Case',
53+
data.Apps.CS2.value,
54+
'USD',
55+
)
3756

38-
def test_market_scraper_invalid_app_without_value(self):
57+
def test_market_scraper_invalid_app_id_without_value(self):
3958
with self.assertRaises(TypeError):
40-
app_id = data.Apps.CS2
41-
scraper.validate_enum_value(app_id, data.Apps, 'app ID')
59+
scraper.market_scraper(
60+
'Dreams & Nightmares Case',
61+
data.Apps.CS2,
62+
data.Currency.USD.value,
63+
)
4264

4365
def test_market_scraper_invalid_currency_without_value(self):
4466
with self.assertRaises(TypeError):
45-
currency = data.Currency.USD
46-
scraper.validate_enum_value(currency, data.Currency, 'currency')
67+
scraper.market_scraper(
68+
'Dreams & Nightmares Case',
69+
data.Apps.CS2.value,
70+
data.Currency.USD,
71+
)
72+
73+
def test_market_scraper_item_name_consist_solely_of_whitespace(self):
74+
with self.assertRaises(ValueError):
75+
scraper.market_scraper(
76+
' ',
77+
data.Apps.CS2.value,
78+
data.Currency.USD.value,
79+
)
4780

48-
def test_market_scraper_invalid_app(self):
81+
def test_market_scraper_invalid_app_id(self):
4982
with self.assertRaises(ValueError):
50-
app_id = 00000
51-
scraper.validate_enum_value(app_id, data.Apps, 'app ID')
83+
scraper.market_scraper(
84+
'Dreams & Nightmares Case',
85+
00000,
86+
data.Currency.USD.value,
87+
)
5288

5389
def test_market_scraper_invalid_currency(self):
5490
with self.assertRaises(ValueError):
55-
currency = 00000
56-
scraper.validate_enum_value(currency, data.Currency, 'currency')
91+
scraper.market_scraper(
92+
'Dreams & Nightmares Case',
93+
data.Apps.CS2.value,
94+
00000,
95+
)
5796

5897

5998
class TestMarketScraper(unittest.TestCase):

0 commit comments

Comments
 (0)