Skip to content

Commit bfb8b55

Browse files
committed
Add module 1 tasks 1 and task 2
1 parent 5cdfa26 commit bfb8b55

File tree

13 files changed

+994
-138
lines changed

13 files changed

+994
-138
lines changed

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,9 @@ instance/
6868

6969
# Virtualenv
7070
venv/
71+
venv36/
72+
venv37/
73+
venv38/
7174
.venv/
7275

7376
# Datasets

conftest.py

Lines changed: 150 additions & 101 deletions
Original file line numberDiff line numberDiff line change
@@ -1,133 +1,182 @@
1-
import re
21
import ast
2+
import json
33

44
import parso
55
import pytest
66

7+
from collections import OrderedDict
8+
from types import GeneratorType as generator
9+
from itertools import chain
710
from pathlib import Path
8-
from redbaron import RedBaron
9-
from redbaron.utils import indent
1011

12+
from objectpath import Tree
13+
from mongoquery import Query
1114

12-
class SourceCode:
13-
def __init__(self, exists, code):
14-
self.exists = exists
15-
self.code = code
15+
from tests.nodes import convert_node, flatten
16+
from tests.template import Template
1617

1718

1819
class Parser:
19-
def __init__(self, filename):
20-
self.code = ""
21-
self.message = ""
20+
def __init__(self, file_name, nodes):
21+
22+
sensor = Path.cwd() / "sensor"
23+
# ext = sensor / "extensions"
24+
25+
self.data = {
26+
"success": True,
27+
"full_path": "",
28+
"message": "",
29+
"start_pos": 0,
30+
"nodes": nodes,
31+
}
32+
33+
if file_name is not None:
34+
path = lambda root, fn: root / "{}.py".format(fn)
35+
# if file_name == "menu" or file_name == "stats":
36+
# full_path = path(ext, file_name)
37+
if file_name == "sensor":
38+
full_path = path(Path.cwd(), file_name)
39+
else:
40+
full_path = path(sensor, file_name)
41+
42+
grammar = parso.load_grammar()
43+
module = grammar.parse(path=full_path)
44+
self.data["success"] = len(grammar.iter_errors(module)) == 0
2245

23-
error_message = ""
24-
error_start_pos = ""
46+
if self.data["success"]:
47+
self.data["message"] = "Syntax: valid"
48+
if file_name is not None:
49+
self.data["nodes"] = convert_node(ast.parse(full_path.read_text()))
50+
self.data["code"] = full_path.read_text()
2551

26-
if filename == "sensor":
27-
file_path = Path.cwd() / "sensor.py"
2852
else:
29-
file_path = Path.cwd() / "sensor" / "{}.py".format(filename)
53+
self.data["message"] = grammar.iter_errors(module)[0].message
54+
self.data["start_pos"] = grammar.iter_errors(module)[0].start_pos[0]
3055

31-
grammar = parso.load_grammar()
32-
module = grammar.parse(path=file_path.resolve())
33-
self.success = len(grammar.iter_errors(module)) == 0
56+
@property
57+
def nodes(self):
58+
return self.data["nodes"]
3459

35-
if self.success:
36-
with open(file_path.resolve(), "r") as source_code:
37-
self.code = RedBaron(source_code.read())
38-
else:
39-
error_message = grammar.iter_errors(module)[0].message
40-
error_start_pos = grammar.iter_errors(module)[0].start_pos[0]
41-
self.message = "{} on or around line {} in `{}`.".format(
42-
error_message, error_start_pos, file_path.name
43-
)
60+
n = nodes
61+
62+
@property
63+
def success(self):
64+
return self.data["success"]
65+
66+
@property
67+
def code(self):
68+
return self.data["code"]
69+
70+
def query(self, pattern):
71+
nodes = Template(pattern).process(self.code)
72+
if isinstance(nodes, list) and len(nodes) == 1:
73+
nodes = nodes[0]
74+
75+
return Parser(None, nodes)
4476

45-
def get_by_name(self, type, name, code=None):
46-
if code is None:
47-
item = self.code.find_all(type, lambda node: node.name == name)
77+
def query_raw(self, pattern):
78+
nodes = Template(pattern).process(self.code, True)
79+
if isinstance(nodes, list) and len(nodes) == 1:
80+
nodes = [flatten(convert_node(node)) for node in nodes[0].body]
81+
return Parser(None, nodes)
82+
83+
def last_line(self):
84+
return flatten(self.nodes["body"][-1])
85+
86+
@property
87+
def message(self):
88+
return "{} on or around line {} in `{}`.".format(
89+
self.data["message"], self.data["start_pos"], self.data["full_path"]
90+
)
91+
92+
def match(self, template):
93+
return Parser(None, list(filter(Query(template).match, self.nodes)))
94+
95+
def execute(self, expr):
96+
result = Tree(self.nodes).execute(expr)
97+
if isinstance(result, (generator, chain, map)):
98+
process = list(result)
99+
return (
100+
Parser(None, process[0]) if len(process) == 1 else Parser(None, process)
101+
)
48102
else:
49-
item = code.find_all(type, lambda node: node.name == name)
103+
return Parser(None, result)
104+
105+
ex = execute
106+
107+
def exists(self):
108+
return bool(self.nodes)
50109

51-
return SourceCode(True, item[0]) if len(item) > 0 else SourceCode(False, [])
110+
def calls(self):
111+
nodes = self.execute("$.body[@.type is 'Expr' and @.value.type is 'Call']").n
112+
node_list = [nodes] if isinstance(nodes, dict) else nodes
52113

53-
def get_call(self, value, code):
54-
call = code.find("call", lambda node: node.previous.value == value)
55-
return SourceCode(True, call) if call is not None and len(call) > 0 else SourceCode(False, [])
114+
return Parser(None, [flatten(node) for node in node_list])
56115

57-
def get_args(self, code):
58-
return list(
59-
code.find_all("call_argument").map(
60-
lambda node: str(node.target) + ":" + str(node.value).replace("'", '"')
116+
def assign_(self):
117+
return Parser(None, [flatten(self.execute("$.body[@.type is 'Assign']").n)])
118+
119+
def assigns(self):
120+
return Parser(
121+
None,
122+
[flatten(node) for node in self.execute("$.body[@.type is 'Assign']").n],
123+
)
124+
125+
def globals(self, name):
126+
return name in self.execute("$.body[@.type is 'Global'].names").n
127+
128+
def defines(self, name):
129+
return self.execute(
130+
"$.body[@.type is 'FunctionDef' and @.name is '{}'].(name, args, body, decorator_list)".format(
131+
name
61132
)
62133
)
63134

64-
def get_by_value(self, type, value, code=None):
65-
if code is None:
66-
item = self.code.find_all(type, lambda node: str(node.target) == value)
67-
else:
68-
item = code.find_all(type, lambda node: str(node.target) == value)
69-
return SourceCode(True, item[0]) if len(item) > 0 else SourceCode(False, [])
70-
71-
def get_imports(self):
72-
imports = []
73-
self.code.find_all(
74-
"import",
75-
lambda node: node.find_all(
76-
"dotted_as_name", lambda node: imports.append(str(node))
77-
),
135+
def class_(self, name):
136+
return self.execute(
137+
"$.body[@.type is 'ClassDef' and @.name is '{}'].(name, args, body)".format(
138+
name
139+
)
78140
)
79-
return imports
80-
81-
def get_from_import(self, value):
82-
imports = self.code.find_all(
83-
"from_import",
84-
lambda node: "".join(list(node.value.node_list.map(lambda node: str(node))))
85-
== value,
86-
).find_all("name_as_name")
87-
return list(imports.map(lambda node: node.value))
88-
89-
def flatten(self, dictionary):
90-
def _flatten(node):
91-
trimmed = re.sub(r"\"|'", "", node.key.value)
92-
flattened = []
93-
if node.value.type is "list":
94-
for item in node.value.node_list:
95-
if item.type is not "comma":
96-
flattened.append("{}:{}".format(trimmed, str(item)))
97-
else:
98-
flattened.append("{}:{}".format(trimmed, node.value.value))
99-
100-
return flattened
101-
102-
items = list(dictionary.find_all("dictitem").map(lambda node: _flatten(node)))
103-
return [item for sublist in items for item in sublist]
104-
105-
def get_conditional(self, values, type, nested=False):
106-
def flat(node):
107-
if node.type == "comparison":
108-
return "{}:{}:{}".format(
109-
str(node.first).replace("'", '"'),
110-
str(node.value).replace(" ", ":"),
111-
str(node.second).replace("'", '"'),
112-
)
113-
elif node.type == "unitary_operator":
114-
return "{}:{}".format(
115-
str(node.value), str(node.target).replace("'", '"')
116-
)
117-
118-
nodes = self.code.value if nested else self.code
119-
for value in values:
120-
final_node = nodes.find_all(type).find(
121-
["comparison", "unitary_operator"], lambda node: flat(node) == value
141+
142+
def decorators(self):
143+
return Parser(None, [flatten(self.execute("$.decorator_list").n)])
144+
145+
def returns(self, name):
146+
return name == self.execute("$.body[@.type is 'Return'].value.id").n
147+
148+
def returns_call(self):
149+
return Parser(None, [flatten(self.execute("$.body[@.type is 'Return']").n)])
150+
151+
def method(self, name):
152+
return self.execute(
153+
"$..body[@.type is 'FunctionDef' and @.name is '{}']".format(name)
154+
)
155+
156+
def has_arg(self, name, pos=0):
157+
nodes = self.execute("$.args.args.arg").n
158+
return nodes[pos] if isinstance(nodes, list) else nodes
159+
160+
def imports(self, name):
161+
return name in self.execute("$.body[@.type is 'Import'].names..name").n
162+
163+
def for_(self):
164+
for_body = self.execute("$.body[@.type is 'For'].body").n
165+
iterators = self.execute("$.body[@.type is 'For'].(target, iter)").n
166+
return Parser(None, [flatten(for_body), flatten(iterators)])
167+
168+
def from_imports(self, mod, alias):
169+
nodes = self.execute(
170+
"$.body[@.type is 'ImportFrom' and @.module is '{}'].names..name".format(
171+
mod
122172
)
123-
if final_node is not None:
124-
return final_node
125-
return None
173+
).n
174+
return alias in (nodes if isinstance(nodes, list) else [nodes])
126175

127176

128177
@pytest.fixture
129178
def parse():
130-
def _parse(filename):
131-
return Parser(filename)
179+
def _parse(file_name):
180+
return Parser(file_name, {})
132181

133182
return _parse

datasets/SENSOR_ROOM1.csv

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
id,date,time,temperature,humidity,energy_usage,particulate,room
1+
id,date,time,temperature,humidity,energy_usage,particulate,area
22
1,5/7/2020,0:01,60,0.8,0xffe,6.2E+00,1
33
2,5/7/2020,0:01,60,0.8,0xff3,6.3E+00,1
44
3,5/7/2020,0:04,60,0.8,0xfef,6.5E+00,1

datasets/SENSOR_ROOM2.csv

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
id,date,time,temperature,humidity,energy_usage,particulate,room
1+
id,date,time,temperature,humidity,energy_usage,particulate,area
22
1,5/7/2020,0:01,50,0.8,0xffc,8.1E+00,2
33
2,5/7/2020,0:02,50,0.53,0xffb,8.1E+00,2
44
3,5/7/2020,0:02,50,0.82,0xff3,8.5E+00,2

requirements.txt

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,9 @@
11
docutils==0.16
2-
Markdown==3.1.1
3-
parso==0.6.0
4-
pytest==5.3.5
2+
Markdown==3.2.1
3+
mongoquery==1.3.6
4+
objectpath==0.6.1
5+
parso==0.6.2
6+
pytest==5.4.1
57
pytest-json-report==1.2.1
6-
pytest-metadata==1.8.0
7-
pytest-sugar==0.9.2
8-
PyYAML==5.3
9-
redbaron==0.9.2
10-
typer==0.0.8
8+
PyYAML==5.3.1
9+
typer==0.1.1

sensor.py

Whitespace-only changes.

sensor/load_data.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
# Module 1: Load data from files
2+
import os
3+
import glob
4+
import csv
5+
6+
def load_sensor_data():
7+
sensor_data = []

sensor/load_info.py

Lines changed: 0 additions & 1 deletion
This file was deleted.
File renamed without changes.

0 commit comments

Comments
 (0)