Skip to content

Commit e6a09e4

Browse files
author
root
committed
update docs and examples
1 parent 65ccd8d commit e6a09e4

File tree

1 file changed

+110
-18
lines changed

1 file changed

+110
-18
lines changed

README.md

Lines changed: 110 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -59,24 +59,116 @@ pip install python-newtype
5959
### Basic Usage
6060

6161
```python
62-
from newtype import NewType
63-
64-
# Create a wrapped string type with additional functionality
65-
class EnhancedStr(NewType(str)):
66-
def reverse(self):
67-
return self[::-1]
68-
69-
def count_words(self):
70-
return len(self.split())
71-
72-
# Use the enhanced type
73-
text = EnhancedStr("Hello World")
74-
print(text.reverse()) # "dlroW olleH"
75-
print(text.count_words()) # 2
76-
77-
# Original string methods still work
78-
print(text.upper()) # "HELLO WORLD"
79-
print(text.split()) # ["Hello", "World"]
62+
import pytest
63+
import re
64+
from newtype import NewType, newtype_exclude
65+
66+
67+
class EmailStr(NewType(str)):
68+
# you can define `__slots__` to save space
69+
__slots__ = (
70+
'_local_part',
71+
'_domain_part',
72+
)
73+
74+
def __init__(self, value: str):
75+
super().__init__()
76+
if "@" not in value:
77+
raise TypeError("`EmailStr` requires a '@' symbol within")
78+
self._local_part, self._domain_part = value.split("@")
79+
80+
@newtype_exclude
81+
def __str__(self):
82+
return f"<Email - Local Part: {self.local_part}; Domain Part: {self.domain_part}>"
83+
84+
@property
85+
def local_part(self):
86+
"""Return the local part of the email address."""
87+
return self._local_part
88+
89+
@property
90+
def domain_part(self):
91+
"""Return the domain part of the email address."""
92+
return self._domain_part
93+
94+
@property
95+
def full_email(self):
96+
"""Return the full email address."""
97+
return str(self)
98+
99+
@classmethod
100+
def from_string(cls, email: str):
101+
"""Create an EmailStr instance from a string."""
102+
return cls(email)
103+
104+
@staticmethod
105+
def is_valid_email(email: str) -> bool:
106+
"""Check if the provided string is a valid email format."""
107+
email_regex = r"^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$"
108+
return re.match(email_regex, email) is not None
109+
110+
111+
def test_emailstr_replace():
112+
"""`EmailStr` uses `str.replace(..)` as its own method, returning an instance of `EmailStr`
113+
if the resultant `str` instance is a value `EmailStr`.
114+
"""
115+
peter_email = EmailStr("peter@gmail.com")
116+
smith_email = EmailStr("smith@gmail.com")
117+
118+
with pytest.raises(Exception):
119+
# this raises because `peter_email` is no longer an instance of `EmailStr`
120+
peter_email = peter_email.replace("peter@gmail.com", "petergmail.com")
121+
122+
# this works because the entire email can be 'replaced'
123+
james_email = smith_email.replace("smith@gmail.com", "james@gmail.com")
124+
125+
# comparison with `str` is built-in
126+
assert james_email == "james@gmail.com"
127+
128+
# `james_email` is still an `EmailStr`
129+
assert isinstance(james_email, EmailStr)
130+
131+
# this works because the local part can be 'replaced'
132+
jane_email = james_email.replace("james", "jane")
133+
134+
# `jane_email` is still an `EmailStr`
135+
assert isinstance(jane_email, EmailStr)
136+
assert jane_email == "jane@gmail.com"
137+
138+
139+
def test_emailstr_properties_methods():
140+
"""Test the property, class method, and static method of EmailStr."""
141+
# Test property
142+
email = EmailStr("test@example.com")
143+
# `property` is not coerced to `EmailStr`
144+
assert email.full_email == "<Email - Local Part: test; Domain Part: example.com>"
145+
assert isinstance(email.full_email, str)
146+
# `property` is not coerced to `EmailStr`
147+
assert not isinstance(email.full_email, EmailStr)
148+
assert email.local_part == "test"
149+
assert email.domain_part == "example.com"
150+
151+
# Test class method
152+
email_from_string = EmailStr.from_string("classmethod@example.com")
153+
# `property` is not coerced to `EmailStr`
154+
assert (
155+
email_from_string.full_email
156+
== "<Email - Local Part: classmethod; Domain Part: example.com>"
157+
)
158+
assert email_from_string.local_part == "classmethod"
159+
assert email_from_string.domain_part == "example.com"
160+
161+
# Test static method
162+
assert EmailStr.is_valid_email("valid.email@example.com") is True
163+
assert EmailStr.is_valid_email("invalid-email.com") is False
164+
165+
166+
def test_email_str__slots__():
167+
email = EmailStr("test@example.com")
168+
169+
with pytest.raises(AttributeError):
170+
email.hi = "bye"
171+
assert email.hi == "bye"
80172
```
81173

82174
## Documentation

0 commit comments

Comments
 (0)