css_types.py 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407
  1. """CSS selector structure items."""
  2. import copyreg
  3. from .pretty import pretty
  4. from typing import Any, Type, Tuple, Union, Dict, Iterator, Hashable, Optional, Pattern, Iterable, Mapping
  5. __all__ = (
  6. 'Selector',
  7. 'SelectorNull',
  8. 'SelectorTag',
  9. 'SelectorAttribute',
  10. 'SelectorContains',
  11. 'SelectorNth',
  12. 'SelectorLang',
  13. 'SelectorList',
  14. 'Namespaces',
  15. 'CustomSelectors'
  16. )
  17. SEL_EMPTY = 0x1
  18. SEL_ROOT = 0x2
  19. SEL_DEFAULT = 0x4
  20. SEL_INDETERMINATE = 0x8
  21. SEL_SCOPE = 0x10
  22. SEL_DIR_LTR = 0x20
  23. SEL_DIR_RTL = 0x40
  24. SEL_IN_RANGE = 0x80
  25. SEL_OUT_OF_RANGE = 0x100
  26. SEL_DEFINED = 0x200
  27. SEL_PLACEHOLDER_SHOWN = 0x400
  28. class Immutable:
  29. """Immutable."""
  30. __slots__: Tuple[str, ...] = ('_hash',)
  31. _hash: int
  32. def __init__(self, **kwargs: Any) -> None:
  33. """Initialize."""
  34. temp = []
  35. for k, v in kwargs.items():
  36. temp.append(type(v))
  37. temp.append(v)
  38. super(Immutable, self).__setattr__(k, v)
  39. super(Immutable, self).__setattr__('_hash', hash(tuple(temp)))
  40. @classmethod
  41. def __base__(cls) -> "Type[Immutable]":
  42. """Get base class."""
  43. return cls
  44. def __eq__(self, other: Any) -> bool:
  45. """Equal."""
  46. return (
  47. isinstance(other, self.__base__()) and
  48. all([getattr(other, key) == getattr(self, key) for key in self.__slots__ if key != '_hash'])
  49. )
  50. def __ne__(self, other: Any) -> bool:
  51. """Equal."""
  52. return (
  53. not isinstance(other, self.__base__()) or
  54. any([getattr(other, key) != getattr(self, key) for key in self.__slots__ if key != '_hash'])
  55. )
  56. def __hash__(self) -> int:
  57. """Hash."""
  58. return self._hash
  59. def __setattr__(self, name: str, value: Any) -> None:
  60. """Prevent mutability."""
  61. raise AttributeError("'{}' is immutable".format(self.__class__.__name__))
  62. def __repr__(self) -> str: # pragma: no cover
  63. """Representation."""
  64. return "{}({})".format(
  65. self.__class__.__name__, ', '.join(["{}={!r}".format(k, getattr(self, k)) for k in self.__slots__[:-1]])
  66. )
  67. __str__ = __repr__
  68. def pretty(self) -> None: # pragma: no cover
  69. """Pretty print."""
  70. print(pretty(self))
  71. class ImmutableDict(Mapping[Any, Any]):
  72. """Hashable, immutable dictionary."""
  73. def __init__(
  74. self,
  75. arg: Union[Dict[Any, Any], Iterable[Tuple[Any, Any]]]
  76. ) -> None:
  77. """Initialize."""
  78. self._validate(arg)
  79. self._d = dict(arg)
  80. self._hash = hash(tuple([(type(x), x, type(y), y) for x, y in sorted(self._d.items())]))
  81. def _validate(self, arg: Union[Dict[Any, Any], Iterable[Tuple[Any, Any]]]) -> None:
  82. """Validate arguments."""
  83. if isinstance(arg, dict):
  84. if not all([isinstance(v, Hashable) for v in arg.values()]):
  85. raise TypeError('{} values must be hashable'.format(self.__class__.__name__))
  86. elif not all([isinstance(k, Hashable) and isinstance(v, Hashable) for k, v in arg]):
  87. raise TypeError('{} values must be hashable'.format(self.__class__.__name__))
  88. def __iter__(self) -> Iterator[Any]:
  89. """Iterator."""
  90. return iter(self._d)
  91. def __len__(self) -> int:
  92. """Length."""
  93. return len(self._d)
  94. def __getitem__(self, key: Any) -> Any:
  95. """Get item: `namespace['key']`."""
  96. return self._d[key]
  97. def __hash__(self) -> int:
  98. """Hash."""
  99. return self._hash
  100. def __repr__(self) -> str: # pragma: no cover
  101. """Representation."""
  102. return "{!r}".format(self._d)
  103. __str__ = __repr__
  104. class Namespaces(ImmutableDict):
  105. """Namespaces."""
  106. def __init__(self, arg: Union[Dict[str, str], Iterable[Tuple[str, str]]]) -> None:
  107. """Initialize."""
  108. super().__init__(arg)
  109. def _validate(self, arg: Union[Dict[str, str], Iterable[Tuple[str, str]]]) -> None:
  110. """Validate arguments."""
  111. if isinstance(arg, dict):
  112. if not all([isinstance(v, str) for v in arg.values()]):
  113. raise TypeError('{} values must be hashable'.format(self.__class__.__name__))
  114. elif not all([isinstance(k, str) and isinstance(v, str) for k, v in arg]):
  115. raise TypeError('{} keys and values must be Unicode strings'.format(self.__class__.__name__))
  116. class CustomSelectors(ImmutableDict):
  117. """Custom selectors."""
  118. def __init__(self, arg: Union[Dict[str, str], Iterable[Tuple[str, str]]]) -> None:
  119. """Initialize."""
  120. super().__init__(arg)
  121. def _validate(self, arg: Union[Dict[str, str], Iterable[Tuple[str, str]]]) -> None:
  122. """Validate arguments."""
  123. if isinstance(arg, dict):
  124. if not all([isinstance(v, str) for v in arg.values()]):
  125. raise TypeError('{} values must be hashable'.format(self.__class__.__name__))
  126. elif not all([isinstance(k, str) and isinstance(v, str) for k, v in arg]):
  127. raise TypeError('{} keys and values must be Unicode strings'.format(self.__class__.__name__))
  128. class Selector(Immutable):
  129. """Selector."""
  130. __slots__ = (
  131. 'tag', 'ids', 'classes', 'attributes', 'nth', 'selectors',
  132. 'relation', 'rel_type', 'contains', 'lang', 'flags', '_hash'
  133. )
  134. tag: Optional['SelectorTag']
  135. ids: Tuple[str, ...]
  136. classes: Tuple[str, ...]
  137. attributes: Tuple['SelectorAttribute', ...]
  138. nth: Tuple['SelectorNth', ...]
  139. selectors: Tuple['SelectorList', ...]
  140. relation: 'SelectorList'
  141. rel_type: Optional[str]
  142. contains: Tuple['SelectorContains', ...]
  143. lang: Tuple['SelectorLang', ...]
  144. flags: int
  145. def __init__(
  146. self,
  147. tag: Optional['SelectorTag'],
  148. ids: Tuple[str, ...],
  149. classes: Tuple[str, ...],
  150. attributes: Tuple['SelectorAttribute', ...],
  151. nth: Tuple['SelectorNth', ...],
  152. selectors: Tuple['SelectorList', ...],
  153. relation: 'SelectorList',
  154. rel_type: Optional[str],
  155. contains: Tuple['SelectorContains', ...],
  156. lang: Tuple['SelectorLang', ...],
  157. flags: int
  158. ):
  159. """Initialize."""
  160. super().__init__(
  161. tag=tag,
  162. ids=ids,
  163. classes=classes,
  164. attributes=attributes,
  165. nth=nth,
  166. selectors=selectors,
  167. relation=relation,
  168. rel_type=rel_type,
  169. contains=contains,
  170. lang=lang,
  171. flags=flags
  172. )
  173. class SelectorNull(Immutable):
  174. """Null Selector."""
  175. def __init__(self) -> None:
  176. """Initialize."""
  177. super().__init__()
  178. class SelectorTag(Immutable):
  179. """Selector tag."""
  180. __slots__ = ("name", "prefix", "_hash")
  181. name: str
  182. prefix: Optional[str]
  183. def __init__(self, name: str, prefix: Optional[str]) -> None:
  184. """Initialize."""
  185. super().__init__(name=name, prefix=prefix)
  186. class SelectorAttribute(Immutable):
  187. """Selector attribute rule."""
  188. __slots__ = ("attribute", "prefix", "pattern", "xml_type_pattern", "_hash")
  189. attribute: str
  190. prefix: str
  191. pattern: Optional[Pattern[str]]
  192. xml_type_pattern: Optional[Pattern[str]]
  193. def __init__(
  194. self,
  195. attribute: str,
  196. prefix: str,
  197. pattern: Optional[Pattern[str]],
  198. xml_type_pattern: Optional[Pattern[str]]
  199. ) -> None:
  200. """Initialize."""
  201. super().__init__(
  202. attribute=attribute,
  203. prefix=prefix,
  204. pattern=pattern,
  205. xml_type_pattern=xml_type_pattern
  206. )
  207. class SelectorContains(Immutable):
  208. """Selector contains rule."""
  209. __slots__ = ("text", "own", "_hash")
  210. text: Tuple[str, ...]
  211. own: bool
  212. def __init__(self, text: Iterable[str], own: bool) -> None:
  213. """Initialize."""
  214. super().__init__(text=tuple(text), own=own)
  215. class SelectorNth(Immutable):
  216. """Selector nth type."""
  217. __slots__ = ("a", "n", "b", "of_type", "last", "selectors", "_hash")
  218. a: int
  219. n: bool
  220. b: int
  221. of_type: bool
  222. last: bool
  223. selectors: 'SelectorList'
  224. def __init__(self, a: int, n: bool, b: int, of_type: bool, last: bool, selectors: 'SelectorList') -> None:
  225. """Initialize."""
  226. super().__init__(
  227. a=a,
  228. n=n,
  229. b=b,
  230. of_type=of_type,
  231. last=last,
  232. selectors=selectors
  233. )
  234. class SelectorLang(Immutable):
  235. """Selector language rules."""
  236. __slots__ = ("languages", "_hash",)
  237. languages: Tuple[str, ...]
  238. def __init__(self, languages: Iterable[str]):
  239. """Initialize."""
  240. super().__init__(languages=tuple(languages))
  241. def __iter__(self) -> Iterator[str]:
  242. """Iterator."""
  243. return iter(self.languages)
  244. def __len__(self) -> int: # pragma: no cover
  245. """Length."""
  246. return len(self.languages)
  247. def __getitem__(self, index: int) -> str: # pragma: no cover
  248. """Get item."""
  249. return self.languages[index]
  250. class SelectorList(Immutable):
  251. """Selector list."""
  252. __slots__ = ("selectors", "is_not", "is_html", "_hash")
  253. selectors: Tuple[Union['Selector', 'SelectorNull'], ...]
  254. is_not: bool
  255. is_html: bool
  256. def __init__(
  257. self,
  258. selectors: Optional[Iterable[Union['Selector', 'SelectorNull']]] = None,
  259. is_not: bool = False,
  260. is_html: bool = False
  261. ) -> None:
  262. """Initialize."""
  263. super().__init__(
  264. selectors=tuple(selectors) if selectors is not None else tuple(),
  265. is_not=is_not,
  266. is_html=is_html
  267. )
  268. def __iter__(self) -> Iterator[Union['Selector', 'SelectorNull']]:
  269. """Iterator."""
  270. return iter(self.selectors)
  271. def __len__(self) -> int:
  272. """Length."""
  273. return len(self.selectors)
  274. def __getitem__(self, index: int) -> Union['Selector', 'SelectorNull']:
  275. """Get item."""
  276. return self.selectors[index]
  277. def _pickle(p: Any) -> Any:
  278. return p.__base__(), tuple([getattr(p, s) for s in p.__slots__[:-1]])
  279. def pickle_register(obj: Any) -> None:
  280. """Allow object to be pickled."""
  281. copyreg.pickle(obj, _pickle)
  282. pickle_register(Selector)
  283. pickle_register(SelectorNull)
  284. pickle_register(SelectorTag)
  285. pickle_register(SelectorAttribute)
  286. pickle_register(SelectorContains)
  287. pickle_register(SelectorNth)
  288. pickle_register(SelectorLang)
  289. pickle_register(SelectorList)