You're Invited:Meet the Socket Team at RSAC and BSidesSF 2026, March 23–26.RSVP
Socket
Book a DemoSign in
Socket

pysubtypes

Package Overview
Dependencies
Maintainers
1
Versions
22
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

pysubtypes - pypi Package Compare versions

Comparing version
0.3.17
to
0.3.18
+1
-1
PKG-INFO
Metadata-Version: 2.1
Name: pysubtypes
Version: 0.3.17
Version: 0.3.18
Summary: Provides subclasses for common python types with additional functionality and convenience methods.

@@ -5,0 +5,0 @@ Home-page: https://github.com/matthewgdv/subtypes

Metadata-Version: 2.1
Name: pysubtypes
Version: 0.3.17
Version: 0.3.18
Summary: Provides subclasses for common python types with additional functionality and convenience methods.

@@ -5,0 +5,0 @@ Home-page: https://github.com/matthewgdv/subtypes

@@ -19,1 +19,3 @@ simplejson

urllib3
xlsxwriter
msoffcrypto-tool
from setuptools import setup, find_packages
from os import path
__version__ = "0.3.17"
__version__ = "0.3.18"

@@ -6,0 +6,0 @@ here = path.abspath(path.dirname(__file__))

__all__ = [
"cached_property",
"Enum", "ValueEnum", "AutoEnum", "ValueAutoEnum",
"Html",
"Enum", "ValueEnum",
"Html", "Xml",
"Http",

@@ -15,12 +15,12 @@ "Singleton",

"Color",
"Translator"
"Translator", "TranslatableMeta", "DoNotTranslateMeta"
]
from .lazy import cached_property
from .enums import Enum, ValueEnum, AutoEnum, ValueAutoEnum
from .markup import Html
from .enums import Enum, ValueEnum
from .markup import Html, Xml
from .http import Http
from .singleton import Singleton
from .namespace import NameSpace
from .translator import Translator
from .translator import Translator, TranslatableMeta, DoNotTranslateMeta
from .str import Str, BaseStr

@@ -27,0 +27,0 @@ from .list import List, BaseList

from __future__ import annotations
from typing import List, Any, Type
from typing import Any, Callable
from collections.abc import Mapping
import json
from .lazy import cached_property
from .str import Str, Accessor
from .str import RegexAccessor as StrRegexAccessor, Settings
from .translator import Translator
from .translator import TranslatableMeta, DoNotTranslateMeta

@@ -16,8 +14,7 @@ from maybe import Maybe

def is_special_private(name: str) -> bool:
return name.startswith("_") and name.endswith("_")
dict_fields = {attr for attr in dir(dict()) if not attr.startswith("_")}
def is_valid_for_attribute_actions(name: Any, dict_class: Type[Dict]) -> bool:
return isinstance(name, str) and name not in dict_class.settings.dict_fields and not is_special_private(name) and name.isidentifier()
def is_valid_for_attribute_actions(name: Any) -> bool:
return isinstance(name, str) and name not in dict_fields and name.isidentifier()

@@ -31,8 +28,5 @@

"""An accessor class for all regex-related Dict methods"""
settings = StrRegexAccessor.Settings()
def __init__(self, parent: Dict = None) -> None:
default = type(self).settings
self.parent, self.settings = parent, StrRegexAccessor.Settings(dotall=default.dotall, ignorecase=default.ignorecase, multiline=default.multiline)
self.str = Str()
self.parent, self.settings = parent, StrRegexAccessor.Settings()

@@ -83,6 +77,6 @@ def __call__(self, parent: Dict = None, dotall: bool = None, ignorecase: bool = None, multiline: bool = None) -> RegexAccessor:

def copy(self) -> BaseDict:
return type(self)(self.copy())
return type(self)(self)
class Dict(BaseDict):
class Dict(BaseDict, metaclass=TranslatableMeta):
"""

@@ -93,14 +87,10 @@ Subclass of the builtin 'dict' class with where inplace methods like dict.update() return self and therefore allow chaining.

class Settings(Settings):
def __init__(self) -> None:
self.re, self.dict_fields, self.translator, self.recursive = RegexAccessor.settings, {attr for attr in [*dir(dict()), "settings"] if not attr.startswith("_")}, Translator.default, True
class Accessors(Settings):
re = RegexAccessor
settings = Settings()
def __init__(self, *args: Any, **kwargs: Any) -> None:
super().__init__(*args, **kwargs)
if self.settings.recursive:
for key, val in self.items():
self[key] = val
for key, val in self.items():
self[key] = val

@@ -115,6 +105,6 @@ def __getitem__(self, name: str) -> Any:

def __setitem__(self, name: str, val: Any) -> None:
clean_val = self.settings.translator.translate(val) if self.settings.recursive else val
clean_val = type(self).translator.translate(val)
super().__setitem__(name, clean_val)
super().__setitem__(name, clean_val)
if is_valid_for_attribute_actions(name, type(self)):
if is_valid_for_attribute_actions(name):
super().__setattr__(name, clean_val)

@@ -125,13 +115,13 @@

if is_valid_for_attribute_actions(name, type(self)):
if is_valid_for_attribute_actions(name):
super().__delattr__(name)
def __getattr__(self, name: str) -> Dict:
if is_special_private(name):
raise AttributeError(name)
else:
return self[name]
return self[name]
def __setattr__(self, name: str, val: Any) -> None:
if is_special_private(name):
if name in dict_fields:
raise AttributeError(f"Cannot assign to attribute '{type(self).__name__}.{name}'.")
if name.startswith("_") and name.endswith("_"):
super().__setattr__(name, val)

@@ -142,6 +132,9 @@ else:

def __delattr__(self, name: str) -> None:
super().__delattr__(name)
if name in dict_fields:
raise AttributeError(f"Cannot delete attribute '{type(self).__name__}.{name}'.")
if not is_special_private(name):
super().__delitem__(name)
if name.startswith("_") and name.endswith("_"):
super().__delattr__(name)
else:
del self[name]

@@ -157,5 +150,5 @@ def _factory_(self, name: str) -> Dict:

@cached_property
@property
def re(self) -> RegexAccessor:
return RegexAccessor(parent=self)
return self.Accessors.re(parent=self)

@@ -173,7 +166,4 @@ def to_json(self, indent: int = 4, **kwargs: Any) -> str:

class DefaultDict(Dict):
class DefaultDict(Dict, metaclass=DoNotTranslateMeta):
def _factory_(self, name: str) -> DefaultDict:
return type(self)()
Translator.translations[dict] = Dict
from __future__ import annotations
from typing import Any, List
from typing import Any
import enum
import aenum
class EnumMeta(aenum.EnumMeta):
# Implementation of EnumMeta.__new__() and EnumMeta.__prepare__() alongside subtypes.Enum inheriting from enum.Enum unnecessarily
# when it already inherits from aenum.Enum which is a subclass of enum.Enum, is done so that the goddamn PyCharm type checker
# is satisfied that subtypes.Enum is, in fact, a freaking enumeration. Otherwise it just doesn't get it.
def __new__(mcs, name: str, bases: tuple, namespace: dict, init: Any = None, start: Any = None, settings: tuple = ()) -> EnumMeta:
if enum.Enum in bases:
bases = tuple(base for base in bases if not base == enum.Enum)
return super().__new__(mcs, name, bases, namespace, init, start, settings)
@classmethod
def __prepare__(mcs, name: str, bases: tuple, init: Any = None, start: Any = None, settings: tuple = ()) -> EnumMeta:
if enum.Enum in bases:
(bases := list(bases)).remove(enum.Enum)
bases = tuple(bases)
return super().__prepare__(name, bases, init, start, settings)
class EnumMeta(enum.EnumMeta):
def __repr__(cls) -> str:

@@ -51,6 +31,2 @@ return f"{cls.__name__}[{', '.join([f'{member.name}={repr(member.value)}' for member in cls])}]"

def extend(cls, name: str, value: Any) -> None:
"""Extend this Enum with an additional member created from the given name and value."""
aenum.extend_enum(cls, name, value)
def is_enum(cls, candidate: Any) -> bool:

@@ -74,3 +50,3 @@ """Returns True if the candidate is a subclass of Enum, otherwise returns False."""

class BaseEnum(aenum.Enum):
class BaseEnum(enum.Enum):
def __repr__(self) -> str:

@@ -104,16 +80,1 @@ return f"{type(self).__name__}(name={self.name}, value={repr(self.value)})"

"""A subclass of subtypes.Enum. Attribute access on descendants of this class returns the value corresponding to that name, rather than returning the member."""
class BaseAutoEnum(aenum.Enum):
_settings_ = aenum.AutoValue
def _generate_next_value_(name: str, start: str, count: str, last_values: list[str]) -> str:
return name.lower()
class AutoEnum(Enum, BaseAutoEnum):
"""A subclass of subtypes.Enum. Automatically uses _generate_next_value_ when values are missing"""
class ValueAutoEnum(BaseEnum, BaseAutoEnum, metaclass=ValueEnumMeta):
"""A subclass of subtypes.ValueEnum. Missing values are automatically supplied by _generate_next_value_."""
from __future__ import annotations
import contextlib
import itertools

@@ -8,3 +7,3 @@ import io

import os
from typing import Any, Collection, List, Dict, Union, Type, Iterable, TypeVar, Callable, Iterator, cast, TYPE_CHECKING
from typing import Any, Collection, Union, Type, Iterable, TypeVar, Callable, cast, TYPE_CHECKING
import pathlib

@@ -15,5 +14,4 @@

from pandas.io.sql import SQLTable, pandasSQL_builder
from pandas.io.excel._xlsxwriter import _XlsxWriter
from pandas.io.excel._xlsxwriter import XlsxWriter
from pandas.core.indexes.base import Index
import numpy as np
from maybe import Maybe

@@ -66,2 +64,3 @@

# noinspection PyFinal
class Frame(pd.DataFrame):

@@ -177,3 +176,4 @@ columns: Union[Index, Iterable]

def convert_dtypes(self) -> Frame:
def convert_dtypes(self, infer_objects: bool = True, convert_string: bool = True, convert_integer: bool = True, convert_boolean: bool = True, convert_floating: bool = True,) -> Frame:
print(type(super().convert_dtypes()))
return type(self)(super().convert_dtypes())

@@ -385,3 +385,3 @@

class ExcelWriter(_XlsxWriter):
class ExcelWriter(XlsxWriter):
def __init__(self, filepath: PathLike) -> None:

@@ -388,0 +388,0 @@ super().__init__(os.fspath(filepath), engine="xlsxwriter", mode="w")

@@ -10,3 +10,3 @@ from __future__ import annotations

from requests.exceptions import HTTPError
from requests.compat import quote, quote_plus
from urllib.parse import quote, quote_plus
from requests.adapters import HTTPAdapter

@@ -17,3 +17,3 @@

from .enums import Enum
from .translator import Translator
from .translator import TranslatableMeta

@@ -30,3 +30,3 @@

try:
return Translator.default.translate(super().json())
return TranslatableMeta.translator.translate(super().json())
except (json.JSONDecodeError, simplejson.JSONDecodeError):

@@ -33,0 +33,0 @@ return None

@@ -5,3 +5,3 @@ from __future__ import annotations

import json
from typing import Any, Iterable, Iterator, List, Callable, Union
from typing import Any, Iterable, Iterator, Callable, Union

@@ -13,3 +13,3 @@ from .lazy import cached_property

from .str import Accessor, Settings
from .translator import Translator
from .translator import TranslatableMeta

@@ -111,2 +111,3 @@

# noinspection PyArgumentList
class BaseList(list):

@@ -175,7 +176,7 @@ """

def copy(self):
def copy(self) -> BaseList:
return type(self)(self)
class List(BaseList):
class List(BaseList, metaclass=TranslatableMeta):
"""

@@ -189,16 +190,11 @@ Subclass of the builtin 'list' class with additional useful methods. All the 'list' class inplace methods return self and therefore allow chaining when called from this class.

class Settings(Settings):
translator, recursive = Translator.default, True
def __init__(self, iterable: Iterable = None) -> None:
super().__init__(iterable) if iterable is not None else super().__init__()
self.settings = self.Settings()
if self.settings.recursive:
for index, val in enumerate(self):
self[index] = self.settings.translator.translate(val)
for index, val in enumerate(self):
self[index] = type(self).translator.translate(val)
@cached_property
def slice(self) -> SliceAccessor:
return SliceAccessor(parent=self)
return self.Accessors.slice(parent=self)

@@ -254,3 +250,3 @@ @cached_property

def flatten(self) -> List:
"""Recursively traverses any Sequence objects within this List and unpacks them in order into a new flat List."""
"""Recursively traverses any non-textual Sequence objects within this List and unpacks them in order into a new flat List."""
return self._flatten_more(iterable=self, output=type(self)())

@@ -274,4 +270,1 @@

raise TypeError(f"The following json string resolves to type '{type(item).__name__}', not type '{list.__name__}':\n\n{json_string}")
Translator.translations[list] = List

@@ -8,2 +8,3 @@ from __future__ import annotations

import html_text
from xml.dom.minidom import parseString as parse_xml

@@ -13,29 +14,26 @@ from .str import Str

class Html(BeautifulSoup):
"""A subclass of bs4.BeautifulSoup which uses 'html.parser' as its default parser."""
class Markup(BeautifulSoup):
"""A base class for BeautifulSoup markup classes to inherit from."""
_stack = []
parser = None
def __init__(self, html: str = None, features: str = "html.parser", **kwargs: Any) -> None:
element_classes = kwargs.get("element_classes", {})
element_classes[Tag] = Html.Tag
kwargs["element_classes"] = element_classes
def __init__(self, html: str = None, features: str = None, **kwargs: Any) -> None:
kwargs["element_classes"] = {**{Tag: self.Tag}, **kwargs.get("element_classes", {})}
super().__init__(markup=html or "", features=features or self.parser, **kwargs)
self.tag = TagAccessor(self)
self._stack = []
super().__init__(markup=html or "", features=features, **kwargs)
def __repr__(self, encoding="unicode-escape") -> str:
return str(self)
def __str__(self) -> str:
return Str(prettify_html(self.prettify())).re(multiline=True).sub(r"^( +)", lambda m: m.group(1)*4)
def __enter__(self) -> Html:
Html._stack.append(self)
def __enter__(self) -> Markup:
self._stack.append(self)
return self
def __exit__(self, ex_type: Any, ex_value: Any, ex_traceback: Any) -> None:
Html._stack.pop()
self._stack.pop()
def tag(self, name: str, content: str = None, /, attrs: dict = None, **kwattrs: Any) -> Html.Tag:
def _tag(self, name: str, content: str = None, /, attrs: dict = None, **kwattrs: Any) -> Markup.Tag:
tag = self.new_tag(name=name, attrs=attrs or {}, **kwattrs)
tag._root = self

@@ -45,4 +43,4 @@ if content:

if Html._stack:
Html._stack[-1].append(tag)
if self._stack:
self._stack[-1].append(tag)
else:

@@ -53,2 +51,23 @@ self.append(tag)

class Tag(Tag):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self._root = None
def __enter__(self) -> Markup.Tag:
self._root._stack.append(self)
return self
def __exit__(self, ex_type: Any, ex_value: Any, ex_traceback: Any) -> None:
self._root._stack.pop()
class Html(Markup):
"""A subclass of bs4.BeautifulSoup which uses 'lxml' as its default parser."""
parser = "lxml"
def __str__(self) -> str:
return Str(prettify_html(self.prettify())).re(multiline=True).sub(r"^( +)", lambda m: m.group(1)*4)
@property

@@ -58,8 +77,34 @@ def text(self) -> str:

class Tag(Tag):
def __enter__(self) -> Tag:
Html._stack.append(self)
return self
def __exit__(self, ex_type: Any, ex_value: Any, ex_traceback: Any) -> None:
Html._stack.pop()
class Xml(Markup):
"""A subclass of bs4.BeautifulSoup which uses 'xml' as its default parser."""
parser = "xml"
def __str__(self) -> str:
return Str(parse_xml(super().__str__()).toprettyxml()).re(multiline=True).sub(r"^(\t+)", lambda m: m.group(1).replace("\t", " "))
class TagAccessor:
def __init__(self, parent: Markup) -> None:
self._parent = parent
def __call__(self, name: str, content: str = None, /, attrs: dict = None, **kwattrs: Any) -> Markup.Tag:
return self._parent._tag(name, content, attrs=attrs, **kwattrs)
def __getattr__(self, name) -> TagProxy:
return TagProxy(name, parent=self._parent)
class TagProxy:
def __init__(self, name: str, parent: Markup) -> None:
self._name, self._parent = name, parent
def __call__(self, content: str = None, /, attrs: dict = None, **kwattrs: Any) -> Markup.Tag:
return self._parent._tag(self._name, content, attrs=attrs, **kwattrs)
def __enter__(self) -> Markup.Tag:
return self._parent._tag(self._name).__enter__()
def __exit__(self, ex_type: Any, ex_value: Any, ex_traceback: Any) -> None:
pass

@@ -5,4 +5,6 @@ from __future__ import annotations

import itertools
from functools import reduce
from operator import ior
import re
from typing import Any, Callable, Iterator, Iterable, List, Tuple, Dict, Mapping, Match, Union
from typing import Any, Callable, Iterator, Iterable, Tuple, Mapping, Match, Union
import warnings

@@ -19,3 +21,3 @@

from .enums import Enum
from .translator import Translator
from .translator import TranslatableMeta

@@ -64,8 +66,5 @@ with warnings.catch_warnings():

def to_flag(self) -> re.RegexFlag:
ret = re.RegexFlag(0)
for attr, flag in (self.dotall, re.DOTALL), (self.ignorecase, re.IGNORECASE), (self.multiline, re.MULTILINE):
if attr:
ret |= flag
return ret
def to_flag(self) -> int:
flags = [flag for attr, flag in [(self.dotall, re.DOTALL), (self.ignorecase, re.IGNORECASE), (self.multiline, re.MULTILINE)]]
return reduce(ior, flags)

@@ -155,3 +154,2 @@ def __init__(self, parent: Str = None) -> None:

self.parent = Maybe(parent).else_(self.parent)
self.settings.acronyms = Maybe(acronyms).else_(self.settings.acronyms)
return self

@@ -401,3 +399,3 @@

class Str(BaseStr):
class Str(BaseStr, metaclass=TranslatableMeta):
"""A subclass of the builin 'str' class which supports inplace mutation using item access. Has additional methods and accessor objects with additional methods for casing, regex, fuzzy-matching, trimming, and slicing."""

@@ -411,23 +409,23 @@

class Accessors(Settings):
re, slice, fuzzy = RegexAccessor, SliceAccessor, FuzzyAccessor
re, case, slice, trim, fuzzy = RegexAccessor, CasingAccessor, SliceAccessor, TrimAccessor, FuzzyAccessor
@cached_property
def re(self) -> RegexAccessor:
return RegexAccessor(parent=self)
return self.Accessors.re(parent=self)
@cached_property
def case(self) -> CasingAccessor:
return CasingAccessor(parent=self)
return self.Accessors.case(parent=self)
@cached_property
def slice(self) -> SliceAccessor:
return SliceAccessor(parent=self)
return self.Accessors.slice(parent=self)
@cached_property
def trim(self) -> TrimAccessor:
return TrimAccessor(parent=self)
return self.Accessors.trim(parent=self)
@cached_property
def fuzzy(self) -> FuzzyAccessor:
return FuzzyAccessor(parent=self)
return self.Accessors.fuzzy(parent=self)

@@ -455,4 +453,1 @@ def to_clipboard(self) -> None:

return cls(clipboard.paste())
Translator.translations[str] = Str
from __future__ import annotations
from typing import Any, Dict, Type
from typing import Any, Type
from json import loads

@@ -8,14 +8,10 @@

class Translator:
translations: dict[Type, Type] = {}
default: Translator = None
def __init__(self, translations: dict = None) -> None:
self.translations = translations if translations is not None else self.translations.copy()
self.translations = translations or {}
def __call__(self, item: Any, recursive: bool = False) -> Any:
return (self.translate_recursively if recursive else self.translate)(item)
return self.translate_recursively(item) if recursive else self.translate(item)
def translate(self, item: Any) -> Any:
constructor = self.translations.get(type(item))
return item if constructor is None else constructor(item)
return item if (constructor := self.translations.get(type(item))) is None else constructor(item)

@@ -38,2 +34,11 @@ def translate_recursively(self, item: Any) -> Any:

Translator.default = Translator(translations=Translator.translations)
class TranslatableMeta(type):
translator = Translator()
def __init__(cls, name: str, bases: tuple, namespace: dict) -> None:
cls.translator.translations.update({base: cls for base in cls.mro()[1:-1]})
class DoNotTranslateMeta(TranslatableMeta):
def __init__(cls, name: str, bases: tuple, namespace: dict) -> None:
pass