-
-
Notifications
You must be signed in to change notification settings - Fork 11
Expand file tree
/
Copy pathutils.py
More file actions
120 lines (88 loc) · 2.66 KB
/
utils.py
File metadata and controls
120 lines (88 loc) · 2.66 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
"""
Representer Utilities for Python track.
"""
import ast
import errno
import json
import os
import re
from hashlib import md5
from functools import lru_cache
from pathlib import Path
from typing import NewType
import black
Slug = NewType("Slug", str)
SLUG_RE = re.compile(r"^[a-z]+(-[a-z]+)*$")
def slug(string: str) -> Slug:
"""
Check if the given arg is a valid exercise slug.
"""
if not SLUG_RE.match(string):
raise ValueError(f"Does not match {SLUG_RE.pattern!r}: {string!r}")
return Slug(string)
Directory = NewType("Directory", Path)
def directory(string: str) -> Directory:
"""
Check if the given arg is a readable / writeable directory.
"""
path = Path(string)
if not path.is_dir():
err = errno.ENOENT
msg = os.strerror(err)
raise FileNotFoundError(err, f"{msg}: {string!r}")
if not os.access(path, os.R_OK | os.W_OK):
err = errno.EACCES
msg = os.strerror(err)
raise PermissionError(err, f"{msg}: {string!r}")
return Directory(path)
@lru_cache()
def md5sum(data: str) -> str:
"""
Return the md5 sum of the given string.
Somehow, the normalizer code is passing Ellipsis objects
to this function. This causes issues with encoding:
AttributeError: 'ellipsis' object has no attribute 'encode',
requiring the guard below until we can stop the normalizer from
passing the object.
See L360 in normalizer.py for the TODO.
"""
if not isinstance(data, str):
return
else:
return md5(data.encode("utf-8")).hexdigest()
def single_space(text: str) -> str:
"""
Remove any blank lines in the text.
"""
return re.sub(r"^\n{2,}", "\n", text)
def reformat(text: str) -> str:
"""
Reformat a Python string with Black.
"""
return black.format_str(single_space(text), mode=black.FileMode())
def to_json(data: dict) -> str:
"""
Reformat to pretty-print JSON for writing to disk.
"""
return json.dumps(data, indent=4, sort_keys=False)
def parse(source: str) -> ast.AST:
"""
Wrapper around ast.parse.
Preserves type annotations.
"""
return ast.parse(source, type_comments=True, feature_version=(3,13))
def dump_tree(tree: ast.AST) -> str:
"""
Dump a formatted string of the AST.
"""
return ast.dump(tree, annotate_fields=False, include_attributes=True, indent=2)
def dump_ast(tree: ast.AST) -> str:
"""
dump an un-formatted string of the AST.
"""
return ast.dump(tree, annotate_fields=False, include_attributes=True)
def to_source(tree: ast.AST) -> str:
"""
Dump the AST to generated source doe.
"""
return ast.unparse(tree)