feat: init src codes

This commit is contained in:
2026-02-09 14:47:15 +08:00
parent adac64a2f6
commit 3140fa106d
8 changed files with 229 additions and 2 deletions

4
.gitignore vendored
View File

@@ -86,7 +86,7 @@ ipython_config.py
# pyenv
# For a library or package, you might want to ignore these files since the code is
# intended to run in multiple environments; otherwise, check them in:
# .python-version
.python-version
# pipenv
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
@@ -99,7 +99,7 @@ ipython_config.py
# Similar to Pipfile.lock, it is generally recommended to include uv.lock in version control.
# This is especially recommended for binary packages to ensure reproducibility, and is more
# commonly ignored for libraries.
#uv.lock
uv.lock
# poetry
# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.

19
pyproject.toml Normal file
View File

@@ -0,0 +1,19 @@
[project]
name = "primitive-type"
version = "0.0.1"
description = "Check a value or object if the type of it is primitive, or convert it to other primitive type in Python."
readme = "README.md"
license = { file = "LICENSE" }
authors = [{ name = "CleMooling", email = "clemooling@staringplanet.top" }]
keywords = ["type-checking", "primitive", "conversion"]
classifiers = [
"Programming Language :: Python :: 3.12",
"Programming Language :: Python :: 3.13",
# "Typing :: Typed",
]
requires-python = ">=3.12"
dependencies = ["beartype>=0.22.9"]
[build-system]
requires = ["hatchling"]
build-backend = "hatchling.build"

View File

@@ -0,0 +1,21 @@
"""Entrypoint of this package."""
from primitive_type.types import Primitive, PrimitiveMap
from primitive_type.checker import is_primitive, is_nested_dict
from primitive_type.converter import get_primitive_object, ConvertError
from importlib.metadata import version, PackageNotFoundError
try:
__version__ = version("primitive-type")
except PackageNotFoundError:
__version__ = "unknown"
__all__ = [
"Primitive",
"PrimitiveMap",
"is_primitive",
"is_nested_dict",
"get_primitive_object",
"ConvertError",
]

View File

@@ -0,0 +1,31 @@
"""Utils for checking objects."""
from beartype import beartype
def is_primitive(val: object) -> bool:
"""Check an object if it's a primitive object.
:param val: Any instance for checking.
:return:
* :obj:`True` -- If the object is primitive
* :obj:`False` -- Otherwise.
"""
return isinstance(val, (str, int, float, bool)) or val is None
@beartype
def is_nested_dict(obj: dict) -> bool:
"""Check a dict if it has nested structures.
:param obj: Any dict object for checking.
:return:
* :obj:`True` -- If the dict has nested structures.
* :obj:`False` -- Otherwise.
"""
for k, v in obj.items():
if not is_primitive(v):
return True
return False

View File

@@ -0,0 +1,74 @@
"""A quick and simple converter to convert the type of an object."""
import re
from beartype import beartype
from primitive_type.types import Primitive
class ConvertError(TypeError):
"""An exception should happens when conversion failed."""
pass
@beartype
def get_primitive_object(val: Primitive, obj_type: type[Primitive] = None) -> Primitive:
"""Get a primitive object from a given value.
:param val: The given value wants to be converted.
:param obj_type: The target type of object that finally converted out. Default to :obj:`None` as disabled.
:return:
* :class:`str` -- If given value is a normal string, or convert to if type specified.
* :class:`int` -- The origin object or convert to.
* :class:`float` -- The origin object or convert to.
* :class:`bool` -- The origin object or convert to.
* :obj:`None` -- Only when given value is :obj:`None`.
"""
obj_type_required: bool = obj_type is not None
if val is None:
if obj_type_required:
raise TypeError("Given value is None, could not convert to other type!")
return None
if not obj_type_required:
if not isinstance(val, str):
return val
val_lower = val.lower()
if val_lower == "true":
return True
if val_lower == "false":
return False
if re.fullmatch(r"[+-]?\d+", val):
return int(val)
if re.fullmatch(r"[+-]?(\d+(\.\d*)?|\.\d+)([eE][+-]?\d+)?", val):
return float(val)
return val
try:
if obj_type is str:
return str(val)
if obj_type is bool:
if isinstance(val, str):
return val.lower() == "true"
if isinstance(val, (int, float)):
return val > 0
return bool(val)
if obj_type is int:
return int(float(val)) if isinstance(val, str) else int(val)
if obj_type is float:
return float(val)
except (ValueError, TypeError, SyntaxError):
raise ConvertError(f"Could not convert '{val}' to type '{obj_type}'.")
return val

View File

View File

@@ -0,0 +1,16 @@
"""Type aliases for primitive types."""
type Primitive = str | int | float | bool | None
"""
Type alias for primitive values.
Including: :class:`str`, :class:`int`, :class:`float`, :class:`bool`, and :obj:`None`.
"""
type PrimitiveMap = dict[str, Primitive]
"""Type alias for a mapping of string keys to primitive values.
This dictionary represents a collection of metadata or attributes where each
value is guaranteed to be a :data:`Primitive` type, ensuring type safety
and ease of serialization.
"""

View File

@@ -0,0 +1,66 @@
import unittest
from primitive_type.converter import get_primitive_object, ConvertError
class TestPrimitiveTypeConverter(unittest.TestCase):
# Assertion of conversion tests.
def test_auto_infer_bool(self):
"""Test conversion of bool strings."""
self.assertEqual(get_primitive_object("true"), True)
self.assertEqual(get_primitive_object("FALSE"), False)
self.assertEqual(get_primitive_object("True"), True)
def test_auto_infer_int(self):
"""Test conversion of integer number strings."""
self.assertEqual(get_primitive_object("123"), 123)
self.assertEqual(get_primitive_object("-456"), -456)
def test_auto_infer_float(self):
"""Test conversion of float number strings."""
self.assertEqual(get_primitive_object("3.14"), 3.14)
self.assertEqual(get_primitive_object(".5"), 0.5)
self.assertEqual(get_primitive_object("1e-10"), 1e-10)
def test_auto_infer_none_and_others(self):
"""Test assertion of conversion for `None` object or other."""
self.assertIsNone(get_primitive_object(None))
self.assertEqual(get_primitive_object("hello"), "hello")
self.assertEqual(get_primitive_object(100), 100)
self.assertEqual(get_primitive_object(3.14), 3.14)
self.assertEqual(get_primitive_object(True), True)
# Explicit conversion tests
def test_explicit_to_str(self):
"""Test explicit convert a value to a `str` value."""
self.assertEqual(get_primitive_object(123, obj_type=str), "123")
self.assertEqual(get_primitive_object(True, obj_type=str), "True")
def test_explicit_to_int(self):
"""Test explicit convert a value to a `int` value."""
self.assertEqual(get_primitive_object("123", obj_type=int), 123)
self.assertEqual(get_primitive_object(3.9, obj_type=int), 3)
# 你的实现中包含 int(float(val)) 逻辑,测试字符串带小数点的转换
self.assertEqual(get_primitive_object("12.3", obj_type=int), 12)
def test_explicit_to_bool(self):
"""Test explicit convert a value to a `bool` value."""
self.assertEqual(get_primitive_object("true", obj_type=bool), True)
self.assertEqual(get_primitive_object(1, obj_type=bool), True)
self.assertEqual(get_primitive_object(0, obj_type=bool), False)
def test_convert_none_error(self):
"""Test raise `TypeError` when wants to convert a `None` object into a value that is not None."""
with self.assertRaises(TypeError):
get_primitive_object(None, obj_type=int)
def test_convert_failure(self):
"""Test raise `ConvertError` when conversion failed."""
with self.assertRaises(ConvertError):
get_primitive_object("abc", obj_type=float)
if __name__ == "__main__":
unittest.main()