ssc_codegen 0.8.23__tar.gz → 0.9.0__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.

Potentially problematic release.


This version of ssc_codegen might be problematic. Click here for more details.

Files changed (53) hide show
  1. {ssc_codegen-0.8.23 → ssc_codegen-0.9.0}/.gitignore +1 -1
  2. {ssc_codegen-0.8.23 → ssc_codegen-0.9.0}/PKG-INFO +1 -1
  3. {ssc_codegen-0.8.23 → ssc_codegen-0.9.0}/pyproject.toml +1 -1
  4. {ssc_codegen-0.8.23 → ssc_codegen-0.9.0}/ssc_codegen/__init__.py +2 -2
  5. {ssc_codegen-0.8.23 → ssc_codegen-0.9.0}/ssc_codegen/ast_build/utils.py +2 -0
  6. {ssc_codegen-0.8.23 → ssc_codegen-0.9.0}/ssc_codegen/converters/js_pure.py +3 -0
  7. {ssc_codegen-0.8.23 → ssc_codegen-0.9.0}/ssc_codegen/converters/py_base.py +21 -4
  8. {ssc_codegen-0.8.23 → ssc_codegen-0.9.0}/ssc_codegen/converters/templates/js_pure.py +9 -1
  9. {ssc_codegen-0.8.23 → ssc_codegen-0.9.0}/ssc_codegen/schema.py +23 -3
  10. {ssc_codegen-0.8.23 → ssc_codegen-0.9.0}/ssc_codegen/static_checker/__init__.py +3 -2
  11. {ssc_codegen-0.8.23 → ssc_codegen-0.9.0}/ssc_codegen/static_checker/callbacks.py +20 -0
  12. {ssc_codegen-0.8.23 → ssc_codegen-0.9.0}/ssc_codegen/tokens.py +1 -0
  13. {ssc_codegen-0.8.23 → ssc_codegen-0.9.0}/LICENSE +0 -0
  14. {ssc_codegen-0.8.23 → ssc_codegen-0.9.0}/README.md +0 -0
  15. {ssc_codegen-0.8.23 → ssc_codegen-0.9.0}/ssc_codegen/_compat.py +0 -0
  16. {ssc_codegen-0.8.23 → ssc_codegen-0.9.0}/ssc_codegen/ast_/__init__.py +0 -0
  17. {ssc_codegen-0.8.23 → ssc_codegen-0.9.0}/ssc_codegen/ast_/base.py +0 -0
  18. {ssc_codegen-0.8.23 → ssc_codegen-0.9.0}/ssc_codegen/ast_/nodes_array.py +0 -0
  19. {ssc_codegen-0.8.23 → ssc_codegen-0.9.0}/ssc_codegen/ast_/nodes_cast.py +0 -0
  20. {ssc_codegen-0.8.23 → ssc_codegen-0.9.0}/ssc_codegen/ast_/nodes_core.py +0 -0
  21. {ssc_codegen-0.8.23 → ssc_codegen-0.9.0}/ssc_codegen/ast_/nodes_filter.py +0 -0
  22. {ssc_codegen-0.8.23 → ssc_codegen-0.9.0}/ssc_codegen/ast_/nodes_selectors.py +0 -0
  23. {ssc_codegen-0.8.23 → ssc_codegen-0.9.0}/ssc_codegen/ast_/nodes_string.py +0 -0
  24. {ssc_codegen-0.8.23 → ssc_codegen-0.9.0}/ssc_codegen/ast_/nodes_validate.py +0 -0
  25. {ssc_codegen-0.8.23 → ssc_codegen-0.9.0}/ssc_codegen/ast_build/__init__.py +0 -0
  26. {ssc_codegen-0.8.23 → ssc_codegen-0.9.0}/ssc_codegen/ast_build/main.py +0 -0
  27. {ssc_codegen-0.8.23 → ssc_codegen-0.9.0}/ssc_codegen/cli/__init__.py +0 -0
  28. {ssc_codegen-0.8.23 → ssc_codegen-0.9.0}/ssc_codegen/cli/cli_callbacks.py +0 -0
  29. {ssc_codegen-0.8.23 → ssc_codegen-0.9.0}/ssc_codegen/cli/cli_utils.py +0 -0
  30. {ssc_codegen-0.8.23 → ssc_codegen-0.9.0}/ssc_codegen/cli/code_callbacks.py +0 -0
  31. {ssc_codegen-0.8.23 → ssc_codegen-0.9.0}/ssc_codegen/cli/consts.py +0 -0
  32. {ssc_codegen-0.8.23 → ssc_codegen-0.9.0}/ssc_codegen/cli/main.py +0 -0
  33. {ssc_codegen-0.8.23 → ssc_codegen-0.9.0}/ssc_codegen/cli/runtime_parse_runners.py +0 -0
  34. {ssc_codegen-0.8.23 → ssc_codegen-0.9.0}/ssc_codegen/compiler.py +0 -0
  35. {ssc_codegen-0.8.23 → ssc_codegen-0.9.0}/ssc_codegen/consts.py +0 -0
  36. {ssc_codegen-0.8.23 → ssc_codegen-0.9.0}/ssc_codegen/converters/__init__.py +0 -0
  37. {ssc_codegen-0.8.23 → ssc_codegen-0.9.0}/ssc_codegen/converters/base.py +0 -0
  38. {ssc_codegen-0.8.23 → ssc_codegen-0.9.0}/ssc_codegen/converters/go_goquery.py +0 -0
  39. {ssc_codegen-0.8.23 → ssc_codegen-0.9.0}/ssc_codegen/converters/helpers.py +0 -0
  40. {ssc_codegen-0.8.23 → ssc_codegen-0.9.0}/ssc_codegen/converters/py_bs4.py +0 -0
  41. {ssc_codegen-0.8.23 → ssc_codegen-0.9.0}/ssc_codegen/converters/py_parsel.py +0 -0
  42. {ssc_codegen-0.8.23 → ssc_codegen-0.9.0}/ssc_codegen/converters/py_selectolax.py +0 -0
  43. {ssc_codegen-0.8.23 → ssc_codegen-0.9.0}/ssc_codegen/converters/templates/__init__.py +0 -0
  44. {ssc_codegen-0.8.23 → ssc_codegen-0.9.0}/ssc_codegen/converters/templates/go_goquery.py +0 -0
  45. {ssc_codegen-0.8.23 → ssc_codegen-0.9.0}/ssc_codegen/document.py +0 -0
  46. {ssc_codegen-0.8.23 → ssc_codegen-0.9.0}/ssc_codegen/document_utlis.py +0 -0
  47. {ssc_codegen-0.8.23 → ssc_codegen-0.9.0}/ssc_codegen/json_struct.py +0 -0
  48. {ssc_codegen-0.8.23 → ssc_codegen-0.9.0}/ssc_codegen/json_to_scc.py +0 -0
  49. {ssc_codegen-0.8.23 → ssc_codegen-0.9.0}/ssc_codegen/logs.py +0 -0
  50. {ssc_codegen-0.8.23 → ssc_codegen-0.9.0}/ssc_codegen/pseudo_selectors.py +0 -0
  51. {ssc_codegen-0.8.23 → ssc_codegen-0.9.0}/ssc_codegen/selector_utils.py +0 -0
  52. {ssc_codegen-0.8.23 → ssc_codegen-0.9.0}/ssc_codegen/static_checker/base.py +0 -0
  53. {ssc_codegen-0.8.23 → ssc_codegen-0.9.0}/ssc_codegen/str_utils.py +0 -0
@@ -38,7 +38,7 @@ MANIFEST
38
38
  pip-log.txt
39
39
  pip-delete-this-directory.txt
40
40
 
41
- # Unit test / coverage reports
41
+ # Unit tests / coverage reports
42
42
  htmlcov/
43
43
  .tox/
44
44
  .nox/
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: ssc_codegen
3
- Version: 0.8.23
3
+ Version: 0.9.0
4
4
  Summary: Python-dsl code converter to html parser for web scraping
5
5
  Project-URL: Documentation, https://github.com/vypivshiy/selector_schema_codegen#readme
6
6
  Project-URL: Issues, https://github.com/vypivshiy/selector_schema_codegen/issues
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "ssc_codegen"
3
- version = "0.8.23"
3
+ version = "0.9.0"
4
4
  description = "Python-dsl code converter to html parser for web scraping "
5
5
  readme = "README.md"
6
6
  requires-python = ">=3.10"
@@ -3,11 +3,11 @@ from ssc_codegen.document import HTMLDocument, StringDocument, ArrayDocument, As
3
3
  NumericDocument, JsonDocument, BooleanDocument, DocumentFilter
4
4
  from ssc_codegen.json_struct import Json
5
5
  from ssc_codegen.logs import setup_logger
6
- from ssc_codegen.schema import ItemSchema, DictSchema, ListSchema, FlatListSchema
6
+ from ssc_codegen.schema import ItemSchema, DictSchema, ListSchema, FlatListSchema, AccUniqueListSchema
7
7
 
8
8
  setup_logger()
9
9
 
10
- VERSION = "0.8.23"
10
+ VERSION = "0.9.0"
11
11
 
12
12
 
13
13
  class __MISSING(object):
@@ -13,6 +13,7 @@ from ssc_codegen.schema import (
13
13
  DictSchema,
14
14
  ItemSchema,
15
15
  FlatListSchema,
16
+ AccUniqueListSchema,
16
17
  )
17
18
  from ssc_codegen.tokens import VariableType
18
19
 
@@ -59,6 +60,7 @@ def is_template_schema_cls(cls: object) -> bool:
59
60
  DictSchema,
60
61
  ListSchema,
61
62
  BaseSchema,
63
+ AccUniqueListSchema,
62
64
  )
63
65
 
64
66
 
@@ -128,6 +128,7 @@ from ssc_codegen.converters.templates.js_pure import (
128
128
  J2_PRE_XPATH_ALL,
129
129
  J2_PRE_STR_TRIM,
130
130
  J2_IS_XPATH,
131
+ J2_START_PARSE_ACC_LIST,
131
132
  )
132
133
  from ssc_codegen.str_utils import (
133
134
  wrap_backtick,
@@ -283,6 +284,8 @@ def pre_start_parse(node: StartParseMethod) -> str:
283
284
  code += J2_START_PARSE_DICT
284
285
  case StructType.FLAT_LIST:
285
286
  code += J2_START_PARSE_FLAT_LIST
287
+ case StructType.ACC_LIST:
288
+ code += J2_START_PARSE_ACC_LIST.render(exprs=exprs)
286
289
  case _:
287
290
  assert_never(node.parent.struct_type) # type: ignore
288
291
  return code
@@ -351,6 +351,9 @@ def pre_typedef(node: TypeDef) -> str:
351
351
  return f"T_{name} = List[{type_}]"
352
352
  case StructType.ITEM | StructType.LIST:
353
353
  return f'T_{name} = TypedDict("T_{name}", ' + "{"
354
+ case StructType.ACC_LIST:
355
+ # current impl support only str type
356
+ return f"T_{name} = List[str]"
354
357
  case _:
355
358
  assert_never(struct_type)
356
359
  raise NotImplementedError() # noqa
@@ -359,9 +362,7 @@ def pre_typedef(node: TypeDef) -> str:
359
362
  def post_typedef(node: TypeDef) -> str:
360
363
  _, struct_type = node.unpack_args()
361
364
  match struct_type:
362
- case StructType.DICT:
363
- return ""
364
- case StructType.FLAT_LIST:
365
+ case StructType.DICT | StructType.FLAT_LIST | StructType.ACC_LIST:
365
366
  return ""
366
367
  # close TypedDict type
367
368
  case StructType.ITEM | StructType.LIST:
@@ -375,7 +376,11 @@ def pre_typedef_field(node: TypeDefField) -> str:
375
376
  name, var_type, cls_nested, cls_nested_type = node.unpack_args()
376
377
  node.parent = cast(TypeDef, node.parent)
377
378
  # skip generated types
378
- if node.parent.struct_type in (StructType.DICT, StructType.FLAT_LIST):
379
+ if node.parent.struct_type in (
380
+ StructType.DICT,
381
+ StructType.FLAT_LIST,
382
+ StructType.ACC_LIST,
383
+ ):
379
384
  return ""
380
385
  # always string
381
386
  elif name == "__KEY__":
@@ -485,6 +490,18 @@ def post_start_parse(node: StartParseMethod) -> str:
485
490
  continue
486
491
  code += f"{name!r}: self._parse_{name}(self._document),"
487
492
  code += "}"
493
+ case StructType.ACC_LIST:
494
+ code += INDENT_METHOD_BODY + "return list(dict.fromkeys("
495
+ methods = []
496
+ for expr in node.body:
497
+ name = expr.kwargs["name"]
498
+ if name.startswith("__"):
499
+ continue
500
+ # acc list of str (all list are flatten)
501
+ methods.append(f"self._parse_{name}(self._document)")
502
+ code += " + ".join(methods)
503
+ code += "))"
504
+
488
505
  case StructType.LIST:
489
506
  # return [{...} for el in self._split_doc(self.document)]
490
507
  code += INDENT_METHOD_BODY + "return [{"
@@ -13,7 +13,15 @@ throw new Error("Invalid input: Expected a Document, Element, or string");}
13
13
 
14
14
  J2_START_PARSE_DICT = "return Array.from(this._splitDoc(this._doc)).reduce((item, e) => (item[this._parseKey(e)] = this._parseValue(e), item), {});"
15
15
  J2_START_PARSE_FLAT_LIST = "return Array.from(this._splitDoc(this._doc)).map((e) => this._parseItem(e));"
16
-
16
+ J2_START_PARSE_ACC_LIST = Template(
17
+ """
18
+ return [...new Set([
19
+ {% for expr in exprs %}
20
+ this._parse{{ expr.upper_name }}(this._doc),
21
+ {% endfor %}
22
+ ].flat())];
23
+ """
24
+ )
17
25
  J2_START_PARSE_ITEM = Template("""return {
18
26
  {% for expr in exprs %}
19
27
  {{ expr.name }}: this._parse{{ expr.upper_name }}(this._doc),
@@ -22,19 +22,29 @@ class BaseSchema:
22
22
  __SCHEMA_TYPE__: StructType = NotImplemented
23
23
 
24
24
  __SIGNATURE__: dict | list = NotImplemented
25
- """manually write signature attribute"""
25
+ """manually write signature for docstring"""
26
26
 
27
27
  __PRE_VALIDATE__: _T_OPT_FIELD = MISSING_FIELD
28
+ """Optional method for pre validation input document
29
+
30
+ if checks not passed - throw error
31
+ """
28
32
 
29
33
  # DICT, LIST, FLAT_LIST HOOK
30
34
  __SPLIT_DOC__: _T_OPT_FIELD = MISSING_FIELD
35
+ """ListSchema, DictSchema, FlatListSchema method for write split document to parts of elements
36
+ """
31
37
 
32
38
  # FLAT LIST HOOK
33
39
  __ITEM__: _T_OPT_FIELD = MISSING_FIELD
40
+ """FlatListSchema item method for parse items for array"""
34
41
 
35
42
  # DICT HOOKS
36
43
  __KEY__: _T_OPT_FIELD = MISSING_FIELD
44
+ """DictSchema method for parse key """
45
+
37
46
  __VALUE__: _T_OPT_FIELD = MISSING_FIELD
47
+ """DictSchema method for parse value"""
38
48
 
39
49
  __EXCLUDE_SIGNATURE__: _T_OPT_FIELD = MISSING_FIELD
40
50
 
@@ -61,6 +71,9 @@ class BaseSchema:
61
71
  signature = cls._get_list_signature()
62
72
  case StructType.ITEM:
63
73
  signature = cls._get_item_signature()
74
+
75
+ case StructType.ACC_LIST:
76
+ signature = [VariableType.STRING, "..."]
64
77
  case _:
65
78
  # code unreached
66
79
  raise TypeError("Unknown schema type")
@@ -117,8 +130,11 @@ class BaseSchema:
117
130
  signature = []
118
131
  field = cls.__ITEM__
119
132
  if field.stack_last_ret == VariableType.NESTED:
120
- nested_class = cls._get_nested_signature(field)
121
- signature.append(nested_class.__class_signature__())
133
+ name = [e for e in field.stack if e.kind == TokenType.EXPR_NESTED][
134
+ 0
135
+ ].kwargs["schema_name"]
136
+ nested_class = cls.__NESTED_SCHEMAS__[name].__class_signature__()
137
+ signature.append(nested_class)
122
138
  elif field.stack_last_ret == VariableType.JSON:
123
139
  signature[k] = json_struct_to_signature(v.stack[-1].value) # noqa
124
140
  else:
@@ -208,3 +224,7 @@ class DictSchema(BaseSchema):
208
224
 
209
225
  class FlatListSchema(BaseSchema):
210
226
  __SCHEMA_TYPE__ = StructType.FLAT_LIST
227
+
228
+
229
+ class AccUniqueListSchema(BaseSchema):
230
+ __SCHEMA_TYPE__ = StructType.ACC_LIST
@@ -6,7 +6,7 @@ from ssc_codegen.static_checker.callbacks import analyze_schema_item_field, anal
6
6
  analyze_schema_value_field, analyze_field_default_value, analyze_field_type_static, analyze_field_html_queries, \
7
7
  analyze_schema_split_doc_field, analyze_field_split_doc_ret_type, CB_SCHEMA, CB_DOCUMENT, analyze_regex_expr, \
8
8
  analyze_field_key_ret_type, analyze_other_field_type, analyze_dict_schema_fields, analyze_flat_list_schema_fields, \
9
- analyze_jsonify_expr
9
+ analyze_jsonify_expr, analyze_schema_acc_list
10
10
 
11
11
  LOGGER = logging.getLogger("ssc_gen")
12
12
 
@@ -16,7 +16,8 @@ _DEFAULT_CB_SCHEMAS = (
16
16
  analyze_schema_key_field,
17
17
  analyze_schema_item_field,
18
18
  analyze_dict_schema_fields,
19
- analyze_flat_list_schema_fields
19
+ analyze_flat_list_schema_fields,
20
+ analyze_schema_acc_list
20
21
  )
21
22
 
22
23
  _DEFAULT_CB_DOCUMENTS = (
@@ -66,6 +66,26 @@ def analyze_flat_list_schema_fields(sc: Type["BaseSchema"]) -> AnalyzeResult:
66
66
  return AnalyzeResult.ok()
67
67
 
68
68
 
69
+ def analyze_schema_acc_list(sc: Type["BaseSchema"]) -> AnalyzeResult:
70
+ # current impl support only LIST_STRING expr
71
+ if sc.__SCHEMA_TYPE__ != StructType.ACC_LIST:
72
+ return AnalyzeResult.ok()
73
+ skip_fields = {"__SPLIT_DOC__", "__PRE_VALIDATE__"}
74
+ err_fields = {
75
+ name: field
76
+ for name, field in sc.__get_mro_fields__().items()
77
+ if name not in skip_fields
78
+ and field.stack_last_ret != VariableType.LIST_STRING
79
+ }
80
+ if err_fields:
81
+ msg = ""
82
+ for name, field in err_fields.items():
83
+ msg += f"{sc.__name__}.{name} expected type(s) {VariableType.LIST_STRING.name}, got {field.stack_last_ret}"
84
+ msg += "\n"
85
+ return AnalyzeResult.error(msg)
86
+ return AnalyzeResult.ok()
87
+
88
+
69
89
  # schema check segment
70
90
  def analyze_schema_split_doc_field(sc: Type["BaseSchema"]) -> AnalyzeResult:
71
91
  if sc.__SCHEMA_TYPE__ in (
@@ -66,6 +66,7 @@ class StructType(IntEnum):
66
66
  DICT = auto()
67
67
  LIST = auto()
68
68
  FLAT_LIST = auto()
69
+ ACC_LIST = auto()
69
70
 
70
71
 
71
72
  class TokenType(IntEnum):
File without changes
File without changes