webcode-tk 1.2.4__tar.gz → 1.2.7__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {webcode_tk-1.2.4 → webcode_tk-1.2.7}/PKG-INFO +3 -2
- {webcode_tk-1.2.4 → webcode_tk-1.2.7}/pyproject.toml +3 -2
- {webcode_tk-1.2.4 → webcode_tk-1.2.7}/webcode_tk/__init__.py +6 -1
- {webcode_tk-1.2.4 → webcode_tk-1.2.7}/webcode_tk/color_tools.py +1 -1
- {webcode_tk-1.2.4 → webcode_tk-1.2.7}/webcode_tk/contrast_tools.py +74 -10
- {webcode_tk-1.2.4 → webcode_tk-1.2.7}/webcode_tk/css_tools.py +8 -1
- {webcode_tk-1.2.4 → webcode_tk-1.2.7}/webcode_tk/validator_tools.py +46 -2
- {webcode_tk-1.2.4 → webcode_tk-1.2.7}/LICENSE.rst +0 -0
- {webcode_tk-1.2.4 → webcode_tk-1.2.7}/README.rst +0 -0
- {webcode_tk-1.2.4 → webcode_tk-1.2.7}/webcode_tk/animation_tools.py +0 -0
- {webcode_tk-1.2.4 → webcode_tk-1.2.7}/webcode_tk/cascade_tools.py +0 -0
- {webcode_tk-1.2.4 → webcode_tk-1.2.7}/webcode_tk/color_keywords.py +0 -0
- {webcode_tk-1.2.4 → webcode_tk-1.2.7}/webcode_tk/font_tools.py +0 -0
- {webcode_tk-1.2.4 → webcode_tk-1.2.7}/webcode_tk/html_tools.py +0 -0
- {webcode_tk-1.2.4 → webcode_tk-1.2.7}/webcode_tk/sample_report.json +0 -0
- {webcode_tk-1.2.4 → webcode_tk-1.2.7}/webcode_tk/utils.py +0 -0
- {webcode_tk-1.2.4 → webcode_tk-1.2.7}/webcode_tk/ux_tools.py +0 -0
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: webcode-tk
|
|
3
|
-
Version: 1.2.
|
|
4
|
-
Summary:
|
|
3
|
+
Version: 1.2.7
|
|
4
|
+
Summary: A set of tools for inspecting HTML and CSS code
|
|
5
5
|
License: MIT
|
|
6
6
|
License-File: LICENSE.rst
|
|
7
7
|
Keywords: html,validation,css
|
|
@@ -23,6 +23,7 @@ Requires-Dist: lxml (>=6.0.0,<7.0.0)
|
|
|
23
23
|
Requires-Dist: mechanicalsoup (>=1.4.0,<2.0.0)
|
|
24
24
|
Requires-Dist: nltk (>=3.8.1,<4.0.0)
|
|
25
25
|
Requires-Dist: py-readability-metrics (>=1.4.5,<2.0.0)
|
|
26
|
+
Requires-Dist: pytest-mock (>=3.15.1,<4.0.0)
|
|
26
27
|
Requires-Dist: requests (>=2.28.1,<3.0.0)
|
|
27
28
|
Requires-Dist: textatistic (>=0.0.1,<0.0.2)
|
|
28
29
|
Requires-Dist: tinycss2 (>=1.4.0,<2.0.0)
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
[tool.poetry]
|
|
2
2
|
name = "webcode-tk"
|
|
3
|
-
version = "1.2.
|
|
4
|
-
description = ""
|
|
3
|
+
version = "1.2.7"
|
|
4
|
+
description = "A set of tools for inspecting HTML and CSS code"
|
|
5
5
|
authors = ["hundredvisionsguy <cwinikka@gmail.com>"]
|
|
6
6
|
keywords = ["html", "validation", "css", ]
|
|
7
7
|
license = "MIT"
|
|
@@ -26,6 +26,7 @@ nltk = "^3.8.1"
|
|
|
26
26
|
tinycss2 = "^1.4.0"
|
|
27
27
|
lxml = "^6.0.0"
|
|
28
28
|
mechanicalsoup = "^1.4.0"
|
|
29
|
+
pytest-mock = "^3.15.1"
|
|
29
30
|
|
|
30
31
|
[tool.poetry.dev-dependencies]
|
|
31
32
|
pytest = "*"
|
|
@@ -16,6 +16,11 @@
|
|
|
16
16
|
|
|
17
17
|
- **`color_tools`**: processes CSS color related properties and values.
|
|
18
18
|
|
|
19
|
+
- **`contrast_tools`**: calculates contrast results taking into account how
|
|
20
|
+
the background is invisible, but still affects contrast on elements
|
|
21
|
+
even if those elements do not have a background color explicitly
|
|
22
|
+
applied. It also only checks elements with text in the innerHTML.
|
|
23
|
+
|
|
19
24
|
- **`css_tools`**: creates Stylesheet objects that store CSS information.
|
|
20
25
|
|
|
21
26
|
- **`font_tools`**: A collection of functions used to process font-related
|
|
@@ -35,4 +40,4 @@
|
|
|
35
40
|
- **`validator_tools`**: sends HTML or CSS code to the W3C Validator to check
|
|
36
41
|
for errors.
|
|
37
42
|
"""
|
|
38
|
-
__version__ = "1.2.
|
|
43
|
+
__version__ = "1.2.7"
|
|
@@ -409,7 +409,7 @@ def hex_to_decimal(c: str) -> int:
|
|
|
409
409
|
msg += "2 digits"
|
|
410
410
|
raise ValueError(msg)
|
|
411
411
|
if c[0].lower() not in hex_map.keys():
|
|
412
|
-
raise ValueError(f"The value
|
|
412
|
+
raise ValueError(f"The value {repr(c)} is not a valid hex code.")
|
|
413
413
|
c = c.lower()
|
|
414
414
|
ones = hex_map[c[1]]
|
|
415
415
|
sixteens = hex_map[c[0]] * 16
|
|
@@ -158,7 +158,8 @@ def generate_contrast_report(
|
|
|
158
158
|
msg = f"{result}: in {current_file}, the {element} failed WCAG "
|
|
159
159
|
msg += f"{wcag_level} contrast requirements (ratio: "
|
|
160
160
|
msg += f"{actual_ratio}, required: {target_ratio})."
|
|
161
|
-
project_files[current_file]
|
|
161
|
+
if msg not in project_files[current_file]:
|
|
162
|
+
project_files[current_file].append(msg)
|
|
162
163
|
|
|
163
164
|
# Check for html docs with no fails.
|
|
164
165
|
for file in project_files:
|
|
@@ -169,9 +170,21 @@ def generate_contrast_report(
|
|
|
169
170
|
else:
|
|
170
171
|
report.extend(errors)
|
|
171
172
|
else:
|
|
172
|
-
|
|
173
|
-
|
|
173
|
+
# Check for global selectors (html, *, or body) and calculate
|
|
174
|
+
# contrast
|
|
175
|
+
global_color_result = css_tools.get_global_color_report(
|
|
176
|
+
project_path
|
|
177
|
+
)
|
|
178
|
+
if isinstance(global_color_result, list):
|
|
179
|
+
for result in global_color_result:
|
|
180
|
+
if "fail" in result:
|
|
181
|
+
if file in result:
|
|
182
|
+
error_msg = result.split(file)[1]
|
|
183
|
+
msg = f"fail: in {file}, {error_msg.strip()}"
|
|
184
|
+
else:
|
|
185
|
+
msg = f"pass: {file} passes color contrast."
|
|
174
186
|
|
|
187
|
+
report.append(msg)
|
|
175
188
|
return report
|
|
176
189
|
|
|
177
190
|
|
|
@@ -194,7 +207,7 @@ def analyze_contrast(project_folder: str) -> list[dict]:
|
|
|
194
207
|
|
|
195
208
|
# Analyze CSS Files
|
|
196
209
|
for html_doc in parsed_html_docs:
|
|
197
|
-
doc_results = analyze_css(html_doc, css_files)
|
|
210
|
+
doc_results = analyze_css(html_doc, css_files, project_folder)
|
|
198
211
|
project_contrast_results.extend(doc_results)
|
|
199
212
|
return project_contrast_results
|
|
200
213
|
|
|
@@ -368,7 +381,9 @@ def append_style_data(
|
|
|
368
381
|
css_source[html_file].append(data)
|
|
369
382
|
|
|
370
383
|
|
|
371
|
-
def analyze_css(
|
|
384
|
+
def analyze_css(
|
|
385
|
+
html_doc: dict, css_files: list[dict], project_path=""
|
|
386
|
+
) -> list[dict]:
|
|
372
387
|
"""
|
|
373
388
|
Analyzes CSS application and color contrast for a single HTML document.
|
|
374
389
|
|
|
@@ -389,6 +404,9 @@ def analyze_css(html_doc: dict, css_files: list[dict]) -> list[dict]:
|
|
|
389
404
|
filename = html_doc["filename"]
|
|
390
405
|
soup = html_doc["soup"]
|
|
391
406
|
|
|
407
|
+
# Check to see if background color was set globally
|
|
408
|
+
global_bg = detect_document_background(project_path, filename)
|
|
409
|
+
|
|
392
410
|
# Check for CSS sources and create warnings if needed
|
|
393
411
|
warnings = []
|
|
394
412
|
|
|
@@ -404,7 +422,7 @@ def analyze_css(html_doc: dict, css_files: list[dict]) -> list[dict]:
|
|
|
404
422
|
)
|
|
405
423
|
|
|
406
424
|
# Step 1: Apply default styles to all elements
|
|
407
|
-
default_styles = apply_browser_defaults(soup)
|
|
425
|
+
default_styles = apply_browser_defaults(soup, default_bg=global_bg)
|
|
408
426
|
|
|
409
427
|
# Find CSS rules specific to this HTML document
|
|
410
428
|
doc_css_rules = get_css_rules_for_document(filename, css_files)
|
|
@@ -452,10 +470,50 @@ def analyze_css(html_doc: dict, css_files: list[dict]) -> list[dict]:
|
|
|
452
470
|
"warning_impact": warning["impact"],
|
|
453
471
|
}
|
|
454
472
|
doc_results.append(warning_result)
|
|
455
|
-
|
|
456
473
|
return doc_results
|
|
457
474
|
|
|
458
475
|
|
|
476
|
+
def detect_document_background(project_path: str, filename: str) -> str:
|
|
477
|
+
"""
|
|
478
|
+
Detect global background color from CSS rules for this document.
|
|
479
|
+
|
|
480
|
+
Args:
|
|
481
|
+
project_path: Path to project root
|
|
482
|
+
filename: Name of current HTML file
|
|
483
|
+
|
|
484
|
+
Returns:
|
|
485
|
+
str: Background color hex value, or DEFAULT_GLOBAL_BACKGROUND if
|
|
486
|
+
none found
|
|
487
|
+
"""
|
|
488
|
+
# Get global colors for entire project
|
|
489
|
+
global_colors = css_tools.get_project_global_colors(project_path)
|
|
490
|
+
|
|
491
|
+
# Find entry for this specific HTML file
|
|
492
|
+
for file_path, color_data_list in global_colors.items():
|
|
493
|
+
# Match filename (handle both full path and just filename)
|
|
494
|
+
if filename in file_path:
|
|
495
|
+
# color_data_list is a list of dicts with global selector info
|
|
496
|
+
if isinstance(color_data_list, dict):
|
|
497
|
+
color_data_list = [
|
|
498
|
+
color_data_list,
|
|
499
|
+
]
|
|
500
|
+
for color_data in color_data_list:
|
|
501
|
+
# Look for body selector specifically
|
|
502
|
+
if color_data.get("selector") == "body":
|
|
503
|
+
bg_color = color_data.get("background-color")
|
|
504
|
+
if bg_color:
|
|
505
|
+
return bg_color
|
|
506
|
+
|
|
507
|
+
# Also check for html or * selectors
|
|
508
|
+
if color_data.get("selector") in ["html", "*"]:
|
|
509
|
+
bg_color = color_data.get("background-color")
|
|
510
|
+
if bg_color:
|
|
511
|
+
return bg_color
|
|
512
|
+
|
|
513
|
+
# No global background found, use default white
|
|
514
|
+
return DEFAULT_GLOBAL_BACKGROUND
|
|
515
|
+
|
|
516
|
+
|
|
459
517
|
def has_any_css_sources(soup: BeautifulSoup) -> bool:
|
|
460
518
|
"""
|
|
461
519
|
Check if HTML document has any CSS sources (external or internal).
|
|
@@ -483,7 +541,9 @@ def has_any_css_sources(soup: BeautifulSoup) -> bool:
|
|
|
483
541
|
return False
|
|
484
542
|
|
|
485
543
|
|
|
486
|
-
def apply_browser_defaults(
|
|
544
|
+
def apply_browser_defaults(
|
|
545
|
+
soup: BeautifulSoup, default_bg: str = DEFAULT_GLOBAL_BACKGROUND
|
|
546
|
+
) -> dict:
|
|
487
547
|
"""
|
|
488
548
|
Apply browser default styles to all elements with text content.
|
|
489
549
|
|
|
@@ -504,7 +564,7 @@ def apply_browser_defaults(soup: BeautifulSoup) -> dict:
|
|
|
504
564
|
"is_default": True,
|
|
505
565
|
},
|
|
506
566
|
"background-color": {
|
|
507
|
-
"value":
|
|
567
|
+
"value": default_bg,
|
|
508
568
|
"specificity": default_specificity,
|
|
509
569
|
"source": "default",
|
|
510
570
|
"is_default": True,
|
|
@@ -1613,5 +1673,9 @@ def element_name_or_default(property_data: dict) -> str:
|
|
|
1613
1673
|
|
|
1614
1674
|
if __name__ == "__main__":
|
|
1615
1675
|
project_path = "tests/test_files/large_project/"
|
|
1616
|
-
|
|
1676
|
+
project_path = "tests/test_files/contrast_tool_test/"
|
|
1677
|
+
contrast_results = generate_contrast_report(project_path, "AAA")
|
|
1678
|
+
project_path = "tests/test_files/contrast_tool_test/"
|
|
1679
|
+
global_colors = css_tools.get_project_global_colors(project_path)
|
|
1680
|
+
print(global_colors)
|
|
1617
1681
|
print(contrast_results)
|
|
@@ -2458,7 +2458,10 @@ def get_global_color_report(project_dir: str, level="aaa") -> list:
|
|
|
2458
2458
|
if global_color_data:
|
|
2459
2459
|
for item in global_color_data:
|
|
2460
2460
|
file, result = get_color_data(filename, item, level)
|
|
2461
|
-
|
|
2461
|
+
if "fail" in result:
|
|
2462
|
+
passes.append(f"fail: {file} {result}")
|
|
2463
|
+
else:
|
|
2464
|
+
passes.append(f"pass: {file} {result}")
|
|
2462
2465
|
if passes:
|
|
2463
2466
|
details = ""
|
|
2464
2467
|
for detail in passes:
|
|
@@ -2877,3 +2880,7 @@ if __name__ == "__main__":
|
|
|
2877
2880
|
rulesets = get_element_rulesets(
|
|
2878
2881
|
"tests/test_files/cascade_complexities", "figure"
|
|
2879
2882
|
)
|
|
2883
|
+
passing_global_colors = get_global_color_report(project_folder)
|
|
2884
|
+
project_path = "tests/test_files/contrast_tool_test/"
|
|
2885
|
+
global_colors = get_global_color_report(project_path)
|
|
2886
|
+
print(global_colors)
|
|
@@ -96,11 +96,16 @@ def get_markup_validity(file_path: str) -> list:
|
|
|
96
96
|
headers = {
|
|
97
97
|
"content-type": "text/html; charset=utf-8",
|
|
98
98
|
"Accept-Charset": "UTF-8",
|
|
99
|
+
"User-Agent": "python-requests/2.32.5",
|
|
99
100
|
}
|
|
100
101
|
|
|
101
102
|
r = requests.post(w3cURL, data=payload, headers=headers)
|
|
102
|
-
|
|
103
|
-
|
|
103
|
+
try:
|
|
104
|
+
errors = r.json()
|
|
105
|
+
errors = errors.get("messages")
|
|
106
|
+
except Exception:
|
|
107
|
+
# We need to use the web browser
|
|
108
|
+
errors = get_validation_by_browser(file_path)
|
|
104
109
|
|
|
105
110
|
# raise the alarm if the response code is not 200
|
|
106
111
|
if r.status_code != 200:
|
|
@@ -220,6 +225,45 @@ def is_css_valid(validator_results):
|
|
|
220
225
|
return bool(soup.find(id="congrats"))
|
|
221
226
|
|
|
222
227
|
|
|
228
|
+
def get_validation_by_browser(file_path: str) -> list:
|
|
229
|
+
"""Validates HTML using the browser.
|
|
230
|
+
|
|
231
|
+
This function will get the HTML code by file and use the mechanical
|
|
232
|
+
soup browser to get validator results as a ResultSet.
|
|
233
|
+
|
|
234
|
+
Args:
|
|
235
|
+
css_code: CSS code in the form of a string.
|
|
236
|
+
|
|
237
|
+
Returns:
|
|
238
|
+
results: A ResultSet of Tag objects.
|
|
239
|
+
"""
|
|
240
|
+
# Get HTML code from file path
|
|
241
|
+
html_code = clerk.file_to_string(file_path)
|
|
242
|
+
try:
|
|
243
|
+
response = browser.open("https://validator.w3.org/")
|
|
244
|
+
if not response.ok:
|
|
245
|
+
response = browser.open("https://validator.w3.org/nu/#textarea")
|
|
246
|
+
browser.select_form("form")
|
|
247
|
+
browser["doc"] = html_code
|
|
248
|
+
browser.submit_selected()
|
|
249
|
+
results = browser.get_current_page().select("div#results")
|
|
250
|
+
else:
|
|
251
|
+
browser.select_form("#validate-by-input form")
|
|
252
|
+
browser["fragment"] = html_code
|
|
253
|
+
browser.submit_selected()
|
|
254
|
+
results = browser.get_current_page().select("div#results")
|
|
255
|
+
except Exception:
|
|
256
|
+
# Convert the file "no_css_connection.html" into a soup tag object
|
|
257
|
+
no_connection_code = (
|
|
258
|
+
"<h1>Sorry, but we could not make a connection</h1>"
|
|
259
|
+
)
|
|
260
|
+
no_connection_code += "<h2>Please try later</h2>"
|
|
261
|
+
soup = BeautifulSoup(no_connection_code, "lxml")
|
|
262
|
+
# Convert string to result set
|
|
263
|
+
results = soup.contents
|
|
264
|
+
return results
|
|
265
|
+
|
|
266
|
+
|
|
223
267
|
def validate_css(css_code: str) -> bs4.ResultSet:
|
|
224
268
|
"""Validates CSS and returns the results from the css-validator.
|
|
225
269
|
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|