Skip to content

Commit 19fff8c

Browse files
authored
feat(langchain): add ruff rules DTZ (#32021)
See https://docs.astral.sh/ruff/rules/#flake8-datetimez-dtz
1 parent 26c2c8f commit 19fff8c

File tree

8 files changed

+63
-53
lines changed

8 files changed

+63
-53
lines changed

libs/langchain/langchain/chains/query_constructor/parser.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -157,7 +157,7 @@ def float(self, item: Any) -> float:
157157
def date(self, item: Any) -> ISO8601Date:
158158
item = str(item).strip("\"'")
159159
try:
160-
datetime.datetime.strptime(item, "%Y-%m-%d")
160+
datetime.datetime.strptime(item, "%Y-%m-%d") # noqa: DTZ007
161161
except ValueError:
162162
warnings.warn(
163163
"Dates are expected to be provided in ISO 8601 date format "
@@ -173,7 +173,7 @@ def datetime(self, item: Any) -> ISO8601DateTime:
173173
datetime.datetime.strptime(item, "%Y-%m-%dT%H:%M:%S%z")
174174
except ValueError:
175175
try:
176-
datetime.datetime.strptime(item, "%Y-%m-%dT%H:%M:%S")
176+
datetime.datetime.strptime(item, "%Y-%m-%dT%H:%M:%S") # noqa: DTZ007
177177
except ValueError as e:
178178
msg = "Datetime values are expected to be in ISO 8601 format."
179179
raise ValueError(msg) from e

libs/langchain/langchain/output_parsers/datetime.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
from datetime import datetime, timedelta
1+
from datetime import datetime, timedelta, timezone
22

33
from langchain_core.exceptions import OutputParserException
44
from langchain_core.output_parsers import BaseOutputParser
@@ -26,7 +26,7 @@ def get_format_instructions(self) -> str:
2626
)
2727
else:
2828
try:
29-
now = datetime.now()
29+
now = datetime.now(tz=timezone.utc)
3030
examples = comma_list(
3131
[
3232
now.strftime(self.format),
@@ -48,7 +48,7 @@ def get_format_instructions(self) -> str:
4848
def parse(self, response: str) -> datetime:
4949
"""Parse a string into a datetime object."""
5050
try:
51-
return datetime.strptime(response.strip(), self.format)
51+
return datetime.strptime(response.strip(), self.format) # noqa: DTZ007
5252
except ValueError as e:
5353
msg = f"Could not parse datetime string: {response}"
5454
raise OutputParserException(msg) from e

libs/langchain/pyproject.toml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -152,6 +152,7 @@ select = [
152152
"COM", # flake8-commas
153153
"D", # pydocstyle
154154
"DOC", # pydoclint
155+
"DTZ", # flake8-datetimez
155156
"E", # pycodestyle error
156157
"EM", # flake8-errmsg
157158
"F", # pyflakes
@@ -217,6 +218,11 @@ pyupgrade.keep-runtime-typing = true
217218
"langchain/chains/constitutional_ai/principles.py" = [
218219
"E501", # Line too long
219220
]
221+
"**/retrievers/*time_weighted_retriever.py" = [
222+
"DTZ001", # Use of non timezone-aware datetime
223+
"DTZ005", # Use of non timezone-aware datetime
224+
"DTZ006", # Use of non timezone-aware datetime
225+
]
220226

221227
[tool.coverage.run]
222228
omit = ["tests/*"]

libs/langchain/tests/unit_tests/indexes/test_indexing.py

Lines changed: 31 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
from collections.abc import AsyncIterator, Iterable, Iterator, Sequence
2-
from datetime import datetime
2+
from datetime import datetime, timezone
33
from typing import (
44
Any,
55
Optional,
@@ -166,6 +166,10 @@ def upserting_vector_store() -> InMemoryVectorStore:
166166
return InMemoryVectorStore(permit_upserts=True)
167167

168168

169+
_JANUARY_FIRST = datetime(2021, 1, 1, tzinfo=timezone.utc).timestamp()
170+
_JANUARY_SECOND = datetime(2021, 1, 2, tzinfo=timezone.utc).timestamp()
171+
172+
169173
def test_indexing_same_content(
170174
record_manager: SQLRecordManager,
171175
vector_store: InMemoryVectorStore,
@@ -256,7 +260,7 @@ def test_index_simple_delete_full(
256260
with patch.object(
257261
record_manager,
258262
"get_time",
259-
return_value=datetime(2021, 1, 1).timestamp(),
263+
return_value=_JANUARY_FIRST,
260264
):
261265
assert index(loader, record_manager, vector_store, cleanup="full") == {
262266
"num_added": 2,
@@ -268,7 +272,7 @@ def test_index_simple_delete_full(
268272
with patch.object(
269273
record_manager,
270274
"get_time",
271-
return_value=datetime(2021, 1, 1).timestamp(),
275+
return_value=_JANUARY_FIRST,
272276
):
273277
assert index(loader, record_manager, vector_store, cleanup="full") == {
274278
"num_added": 0,
@@ -291,7 +295,7 @@ def test_index_simple_delete_full(
291295
with patch.object(
292296
record_manager,
293297
"get_time",
294-
return_value=datetime(2021, 1, 2).timestamp(),
298+
return_value=_JANUARY_SECOND,
295299
):
296300
assert index(loader, record_manager, vector_store, cleanup="full") == {
297301
"num_added": 1,
@@ -311,7 +315,7 @@ def test_index_simple_delete_full(
311315
with patch.object(
312316
record_manager,
313317
"get_time",
314-
return_value=datetime(2021, 1, 2).timestamp(),
318+
return_value=_JANUARY_SECOND,
315319
):
316320
assert index(loader, record_manager, vector_store, cleanup="full") == {
317321
"num_added": 0,
@@ -341,7 +345,7 @@ async def test_aindex_simple_delete_full(
341345
with patch.object(
342346
arecord_manager,
343347
"aget_time",
344-
return_value=datetime(2021, 1, 1).timestamp(),
348+
return_value=_JANUARY_FIRST,
345349
):
346350
assert await aindex(loader, arecord_manager, vector_store, cleanup="full") == {
347351
"num_added": 2,
@@ -353,7 +357,7 @@ async def test_aindex_simple_delete_full(
353357
with patch.object(
354358
arecord_manager,
355359
"aget_time",
356-
return_value=datetime(2021, 1, 1).timestamp(),
360+
return_value=_JANUARY_FIRST,
357361
):
358362
assert await aindex(loader, arecord_manager, vector_store, cleanup="full") == {
359363
"num_added": 0,
@@ -376,7 +380,7 @@ async def test_aindex_simple_delete_full(
376380
with patch.object(
377381
arecord_manager,
378382
"aget_time",
379-
return_value=datetime(2021, 1, 2).timestamp(),
383+
return_value=_JANUARY_SECOND,
380384
):
381385
assert await aindex(loader, arecord_manager, vector_store, cleanup="full") == {
382386
"num_added": 1,
@@ -396,7 +400,7 @@ async def test_aindex_simple_delete_full(
396400
with patch.object(
397401
arecord_manager,
398402
"aget_time",
399-
return_value=datetime(2021, 1, 2).timestamp(),
403+
return_value=_JANUARY_SECOND,
400404
):
401405
assert await aindex(loader, arecord_manager, vector_store, cleanup="full") == {
402406
"num_added": 0,
@@ -507,7 +511,7 @@ def test_no_delete(
507511
with patch.object(
508512
record_manager,
509513
"get_time",
510-
return_value=datetime(2021, 1, 2).timestamp(),
514+
return_value=_JANUARY_SECOND,
511515
):
512516
assert index(
513517
loader,
@@ -526,7 +530,7 @@ def test_no_delete(
526530
with patch.object(
527531
record_manager,
528532
"get_time",
529-
return_value=datetime(2021, 1, 2).timestamp(),
533+
return_value=_JANUARY_SECOND,
530534
):
531535
assert index(
532536
loader,
@@ -558,7 +562,7 @@ def test_no_delete(
558562
with patch.object(
559563
record_manager,
560564
"get_time",
561-
return_value=datetime(2021, 1, 2).timestamp(),
565+
return_value=_JANUARY_SECOND,
562566
):
563567
assert index(
564568
loader,
@@ -596,7 +600,7 @@ async def test_ano_delete(
596600
with patch.object(
597601
arecord_manager,
598602
"aget_time",
599-
return_value=datetime(2021, 1, 2).timestamp(),
603+
return_value=_JANUARY_SECOND,
600604
):
601605
assert await aindex(
602606
loader,
@@ -615,7 +619,7 @@ async def test_ano_delete(
615619
with patch.object(
616620
arecord_manager,
617621
"aget_time",
618-
return_value=datetime(2021, 1, 2).timestamp(),
622+
return_value=_JANUARY_SECOND,
619623
):
620624
assert await aindex(
621625
loader,
@@ -647,7 +651,7 @@ async def test_ano_delete(
647651
with patch.object(
648652
arecord_manager,
649653
"aget_time",
650-
return_value=datetime(2021, 1, 2).timestamp(),
654+
return_value=_JANUARY_SECOND,
651655
):
652656
assert await aindex(
653657
loader,
@@ -684,7 +688,7 @@ def test_incremental_delete(
684688
with patch.object(
685689
record_manager,
686690
"get_time",
687-
return_value=datetime(2021, 1, 2).timestamp(),
691+
return_value=_JANUARY_SECOND,
688692
):
689693
assert index(
690694
loader,
@@ -710,7 +714,7 @@ def test_incremental_delete(
710714
with patch.object(
711715
record_manager,
712716
"get_time",
713-
return_value=datetime(2021, 1, 2).timestamp(),
717+
return_value=_JANUARY_SECOND,
714718
):
715719
assert index(
716720
loader,
@@ -747,7 +751,7 @@ def test_incremental_delete(
747751
with patch.object(
748752
record_manager,
749753
"get_time",
750-
return_value=datetime(2021, 1, 3).timestamp(),
754+
return_value=datetime(2021, 1, 3, tzinfo=timezone.utc).timestamp(),
751755
):
752756
assert index(
753757
loader,
@@ -803,7 +807,7 @@ def test_incremental_indexing_with_batch_size(
803807
with patch.object(
804808
record_manager,
805809
"get_time",
806-
return_value=datetime(2021, 1, 2).timestamp(),
810+
return_value=_JANUARY_SECOND,
807811
):
808812
assert index(
809813
loader,
@@ -870,7 +874,7 @@ def test_incremental_delete_with_batch_size(
870874
with patch.object(
871875
record_manager,
872876
"get_time",
873-
return_value=datetime(2021, 1, 2).timestamp(),
877+
return_value=_JANUARY_SECOND,
874878
):
875879
assert index(
876880
loader,
@@ -897,7 +901,7 @@ def test_incremental_delete_with_batch_size(
897901
with patch.object(
898902
record_manager,
899903
"get_time",
900-
return_value=datetime(2021, 1, 2).timestamp(),
904+
return_value=_JANUARY_SECOND,
901905
):
902906
assert index(
903907
loader,
@@ -917,7 +921,7 @@ def test_incremental_delete_with_batch_size(
917921
with patch.object(
918922
record_manager,
919923
"get_time",
920-
return_value=datetime(2022, 1, 3).timestamp(),
924+
return_value=datetime(2022, 1, 3, tzinfo=timezone.utc).timestamp(),
921925
):
922926
# Docs with same content
923927
docs = [
@@ -948,7 +952,7 @@ def test_incremental_delete_with_batch_size(
948952
with patch.object(
949953
record_manager,
950954
"get_time",
951-
return_value=datetime(2023, 1, 3).timestamp(),
955+
return_value=datetime(2023, 1, 3, tzinfo=timezone.utc).timestamp(),
952956
):
953957
# Docs with same content
954958
docs = [
@@ -979,7 +983,7 @@ def test_incremental_delete_with_batch_size(
979983
with patch.object(
980984
record_manager,
981985
"get_time",
982-
return_value=datetime(2024, 1, 3).timestamp(),
986+
return_value=datetime(2024, 1, 3, tzinfo=timezone.utc).timestamp(),
983987
):
984988
# Docs with same content
985989
docs = [
@@ -1028,7 +1032,7 @@ async def test_aincremental_delete(
10281032
with patch.object(
10291033
arecord_manager,
10301034
"aget_time",
1031-
return_value=datetime(2021, 1, 2).timestamp(),
1035+
return_value=_JANUARY_SECOND,
10321036
):
10331037
assert await aindex(
10341038
loader.lazy_load(),
@@ -1054,7 +1058,7 @@ async def test_aincremental_delete(
10541058
with patch.object(
10551059
arecord_manager,
10561060
"aget_time",
1057-
return_value=datetime(2021, 1, 2).timestamp(),
1061+
return_value=_JANUARY_SECOND,
10581062
):
10591063
assert await aindex(
10601064
loader.lazy_load(),
@@ -1091,7 +1095,7 @@ async def test_aincremental_delete(
10911095
with patch.object(
10921096
arecord_manager,
10931097
"aget_time",
1094-
return_value=datetime(2021, 1, 3).timestamp(),
1098+
return_value=datetime(2021, 1, 3, tzinfo=timezone.utc).timestamp(),
10951099
):
10961100
assert await aindex(
10971101
loader.lazy_load(),

libs/langchain/tests/unit_tests/output_parsers/test_datetime_parser.py

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,14 +10,13 @@ def test_datetime_output_parser_parse() -> None:
1010
parser = DatetimeOutputParser()
1111

1212
# Test valid input
13-
date = datetime.now()
13+
date = datetime.now() # noqa: DTZ005
1414
datestr = date.strftime(parser.format)
1515
result = parser.parse(datestr)
1616
assert result == date
1717

1818
# Test valid input
1919
parser.format = "%Y-%m-%dT%H:%M:%S"
20-
date = datetime.now()
2120
datestr = date.strftime(parser.format)
2221
result = parser.parse(datestr)
2322
assert (
@@ -31,7 +30,6 @@ def test_datetime_output_parser_parse() -> None:
3130

3231
# Test valid input
3332
parser.format = "%H:%M:%S"
34-
date = datetime.now()
3533
datestr = date.strftime(parser.format)
3634
result = parser.parse(datestr)
3735
assert (

libs/langchain/tests/unit_tests/output_parsers/test_fix.py

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
from datetime import datetime as dt
2+
from datetime import timezone
23
from typing import Any, Callable, Optional, TypeVar
34

45
import pytest
@@ -151,17 +152,17 @@ def test_output_fixing_parser_output_type(
151152
[
152153
(
153154
"2024/07/08",
154-
DatetimeOutputParser(),
155+
DatetimeOutputParser(format="%Y-%m-%dT%H:%M:%S.%f%z"),
155156
NAIVE_FIX_PROMPT | RunnableLambda(lambda _: "2024-07-08T00:00:00.000000Z"),
156-
dt(2024, 7, 8),
157+
dt(2024, 7, 8, tzinfo=timezone.utc),
157158
),
158159
(
159160
# Case: retry_chain.InputType does not have 'instructions' key
160161
"2024/07/08",
161-
DatetimeOutputParser(),
162+
DatetimeOutputParser(format="%Y-%m-%dT%H:%M:%S.%f%z"),
162163
PromptTemplate.from_template("{completion}\n{error}")
163164
| RunnableLambda(lambda _: "2024-07-08T00:00:00.000000Z"),
164-
dt(2024, 7, 8),
165+
dt(2024, 7, 8, tzinfo=timezone.utc),
165166
),
166167
],
167168
)
@@ -188,17 +189,17 @@ def test_output_fixing_parser_parse_with_retry_chain(
188189
[
189190
(
190191
"2024/07/08",
191-
DatetimeOutputParser(),
192+
DatetimeOutputParser(format="%Y-%m-%dT%H:%M:%S.%f%z"),
192193
NAIVE_FIX_PROMPT | RunnableLambda(lambda _: "2024-07-08T00:00:00.000000Z"),
193-
dt(2024, 7, 8),
194+
dt(2024, 7, 8, tzinfo=timezone.utc),
194195
),
195196
(
196197
# Case: retry_chain.InputType does not have 'instructions' key
197198
"2024/07/08",
198-
DatetimeOutputParser(),
199+
DatetimeOutputParser(format="%Y-%m-%dT%H:%M:%S.%f%z"),
199200
PromptTemplate.from_template("{completion}\n{error}")
200201
| RunnableLambda(lambda _: "2024-07-08T00:00:00.000000Z"),
201-
dt(2024, 7, 8),
202+
dt(2024, 7, 8, tzinfo=timezone.utc),
202203
),
203204
],
204205
)

0 commit comments

Comments
 (0)