stac-fastapi.types 2.5.3__tar.gz → 2.5.5__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (25) hide show
  1. {stac_fastapi_types-2.5.3 → stac_fastapi_types-2.5.5}/PKG-INFO +1 -1
  2. {stac_fastapi_types-2.5.3 → stac_fastapi_types-2.5.5}/stac_fastapi/types/core.py +6 -10
  3. stac_fastapi_types-2.5.5/stac_fastapi/types/rfc3339.py +126 -0
  4. {stac_fastapi_types-2.5.3 → stac_fastapi_types-2.5.5}/stac_fastapi/types/version.py +1 -1
  5. {stac_fastapi_types-2.5.3 → stac_fastapi_types-2.5.5}/stac_fastapi.types.egg-info/PKG-INFO +1 -1
  6. {stac_fastapi_types-2.5.3 → stac_fastapi_types-2.5.5}/tests/test_rfc3339.py +5 -1
  7. stac_fastapi_types-2.5.3/stac_fastapi/types/rfc3339.py +0 -104
  8. {stac_fastapi_types-2.5.3 → stac_fastapi_types-2.5.5}/README.md +0 -0
  9. {stac_fastapi_types-2.5.3 → stac_fastapi_types-2.5.5}/setup.cfg +0 -0
  10. {stac_fastapi_types-2.5.3 → stac_fastapi_types-2.5.5}/setup.py +0 -0
  11. {stac_fastapi_types-2.5.3 → stac_fastapi_types-2.5.5}/stac_fastapi/types/__init__.py +0 -0
  12. {stac_fastapi_types-2.5.3 → stac_fastapi_types-2.5.5}/stac_fastapi/types/config.py +0 -0
  13. {stac_fastapi_types-2.5.3 → stac_fastapi_types-2.5.5}/stac_fastapi/types/conformance.py +0 -0
  14. {stac_fastapi_types-2.5.3 → stac_fastapi_types-2.5.5}/stac_fastapi/types/errors.py +0 -0
  15. {stac_fastapi_types-2.5.3 → stac_fastapi_types-2.5.5}/stac_fastapi/types/extension.py +0 -0
  16. {stac_fastapi_types-2.5.3 → stac_fastapi_types-2.5.5}/stac_fastapi/types/links.py +0 -0
  17. {stac_fastapi_types-2.5.3 → stac_fastapi_types-2.5.5}/stac_fastapi/types/requests.py +0 -0
  18. {stac_fastapi_types-2.5.3 → stac_fastapi_types-2.5.5}/stac_fastapi/types/search.py +0 -0
  19. {stac_fastapi_types-2.5.3 → stac_fastapi_types-2.5.5}/stac_fastapi/types/stac.py +0 -0
  20. {stac_fastapi_types-2.5.3 → stac_fastapi_types-2.5.5}/stac_fastapi.types.egg-info/SOURCES.txt +0 -0
  21. {stac_fastapi_types-2.5.3 → stac_fastapi_types-2.5.5}/stac_fastapi.types.egg-info/dependency_links.txt +0 -0
  22. {stac_fastapi_types-2.5.3 → stac_fastapi_types-2.5.5}/stac_fastapi.types.egg-info/not-zip-safe +0 -0
  23. {stac_fastapi_types-2.5.3 → stac_fastapi_types-2.5.5}/stac_fastapi.types.egg-info/requires.txt +0 -0
  24. {stac_fastapi_types-2.5.3 → stac_fastapi_types-2.5.5}/stac_fastapi.types.egg-info/top_level.txt +0 -0
  25. {stac_fastapi_types-2.5.3 → stac_fastapi_types-2.5.5}/tests/test_limit.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: stac-fastapi.types
3
- Version: 2.5.3
3
+ Version: 2.5.5
4
4
  Summary: An implementation of STAC API based on the FastAPI framework.
5
5
  Home-page: https://github.com/stac-utils/stac-fastapi
6
6
  Author: Arturo Engineering
@@ -109,10 +109,10 @@ class BaseTransactionsClient(abc.ABC):
109
109
  ) -> Optional[Union[stac_types.Collection, Response]]:
110
110
  """Perform a complete update on an existing collection.
111
111
 
112
- Called with `PUT /collections/{collection_id}`. It is expected that this item
113
- already exists. The update should do a diff against the saved collection and
114
- perform any necessary updates. Partial updates are not supported by the
115
- transactions extension.
112
+ Called with `PUT /collections/{collection_id}`. It is expected that this
113
+ collection already exists. The update should do a diff against the saved
114
+ collection and perform any necessary updates. Partial updates are not
115
+ supported by the transactions extension.
116
116
 
117
117
  Args:
118
118
  collection_id: id of the existing collection to be updated
@@ -404,9 +404,7 @@ class BaseCoreClient(LandingPageMixin, abc.ABC):
404
404
  "rel": "service-desc",
405
405
  "type": "application/vnd.oai.openapi+json;version=3.0",
406
406
  "title": "OpenAPI service description",
407
- "href": urljoin(
408
- str(request.base_url), request.app.openapi_url.lstrip("/")
409
- ),
407
+ "href": str(request.url_for("openapi")),
410
408
  }
411
409
  )
412
410
 
@@ -416,9 +414,7 @@ class BaseCoreClient(LandingPageMixin, abc.ABC):
416
414
  "rel": "service-doc",
417
415
  "type": "text/html",
418
416
  "title": "OpenAPI service documentation",
419
- "href": urljoin(
420
- str(request.base_url), request.app.docs_url.lstrip("/")
421
- ),
417
+ "href": str(request.url_for("swagger_ui_html")),
422
418
  }
423
419
  )
424
420
 
@@ -0,0 +1,126 @@
1
+ """rfc3339."""
2
+ import re
3
+ from datetime import datetime, timezone
4
+ from typing import Optional, Tuple, Union
5
+
6
+ import iso8601
7
+ from fastapi import HTTPException
8
+ from pystac.utils import datetime_to_str
9
+
10
+ RFC33339_PATTERN = (
11
+ r"^(\d\d\d\d)\-(\d\d)\-(\d\d)(T|t)(\d\d):(\d\d):(\d\d)([.]\d+)?"
12
+ r"(Z|([-+])(\d\d):(\d\d))$"
13
+ )
14
+
15
+ DateTimeType = Union[
16
+ datetime,
17
+ Tuple[datetime, datetime],
18
+ Tuple[datetime, None],
19
+ Tuple[None, datetime],
20
+ ]
21
+
22
+
23
+ def rfc3339_str_to_datetime(s: str) -> datetime:
24
+ """Convert a string conforming to RFC 3339 to a :class:`datetime.datetime`.
25
+
26
+ Uses :meth:`iso8601.parse_date` under the hood.
27
+
28
+ Args:
29
+ s (str) : The string to convert to :class:`datetime.datetime`.
30
+
31
+ Returns:
32
+ str: The datetime represented by the ISO8601 (RFC 3339) formatted string.
33
+
34
+ Raises:
35
+ ValueError: If the string is not a valid RFC 3339 string.
36
+ """
37
+ # Uppercase the string
38
+ s = s.upper()
39
+
40
+ # Match against RFC3339 regex.
41
+ result = re.match(RFC33339_PATTERN, s)
42
+ if not result:
43
+ raise ValueError("Invalid RFC3339 datetime.")
44
+
45
+ # Parse with pyiso8601
46
+ return iso8601.parse_date(s)
47
+
48
+
49
+ def parse_single_date(date_str: str) -> datetime:
50
+ """
51
+ Parse a single RFC3339 date string into a datetime object.
52
+
53
+ Args:
54
+ date_str (str): A string representing the date in RFC3339 format.
55
+
56
+ Returns:
57
+ datetime: A datetime object parsed from the date_str.
58
+
59
+ Raises:
60
+ ValueError: If the date_str is empty or contains the placeholder '..'.
61
+ """
62
+ if ".." in date_str or not date_str:
63
+ raise ValueError("Invalid date format.")
64
+ return rfc3339_str_to_datetime(date_str)
65
+
66
+
67
+ def str_to_interval(interval: Optional[str]) -> Optional[DateTimeType]:
68
+ """
69
+ Extract a tuple of datetime objects from an interval string defined by the OGC API.
70
+ The interval can either be a single datetime or a range with start and end datetime.
71
+
72
+ Args:
73
+ interval (Optional[str]): The interval string to convert to datetime objects,
74
+ or None if no datetime is specified.
75
+
76
+ Returns:
77
+ Optional[DateTimeType]: A tuple of datetime.datetime objects or
78
+ None if input is None.
79
+
80
+ Raises:
81
+ HTTPException: If the string is not valid for various reasons such as being empty,
82
+ having more than one slash, or if date formats are invalid.
83
+ """
84
+ if interval is None:
85
+ return None
86
+
87
+ if not interval:
88
+ raise HTTPException(status_code=400, detail="Empty interval string is invalid.")
89
+
90
+ values = interval.split("/")
91
+ if len(values) > 2:
92
+ raise HTTPException(
93
+ status_code=400,
94
+ detail="Interval string contains more than one forward slash.",
95
+ )
96
+
97
+ try:
98
+ start = parse_single_date(values[0]) if values[0] not in ["..", ""] else None
99
+ end = (
100
+ parse_single_date(values[1])
101
+ if len(values) > 1 and values[1] not in ["..", ""]
102
+ else None
103
+ )
104
+ except (ValueError, iso8601.ParseError) as e:
105
+ raise HTTPException(status_code=400, detail=str(e))
106
+
107
+ if start is None and end is None:
108
+ raise HTTPException(
109
+ status_code=400, detail="Double open-ended intervals are not allowed."
110
+ )
111
+ if start is not None and end is not None and start > end:
112
+ raise HTTPException(
113
+ status_code=400, detail="Start datetime cannot be before end datetime."
114
+ )
115
+
116
+ return start, end
117
+
118
+
119
+ def now_in_utc() -> datetime:
120
+ """Return a datetime value of now with the UTC timezone applied."""
121
+ return datetime.now(timezone.utc)
122
+
123
+
124
+ def now_to_rfc3339_str() -> str:
125
+ """Return an RFC 3339 string representing now."""
126
+ return datetime_to_str(now_in_utc())
@@ -1,2 +1,2 @@
1
1
  """Library version."""
2
- __version__ = "2.5.3"
2
+ __version__ = "2.5.5"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: stac-fastapi.types
3
- Version: 2.5.3
3
+ Version: 2.5.5
4
4
  Summary: An implementation of STAC API based on the FastAPI framework.
5
5
  Home-page: https://github.com/stac-utils/stac-fastapi
6
6
  Author: Arturo Engineering
@@ -1,6 +1,7 @@
1
1
  from datetime import timezone
2
2
 
3
3
  import pytest
4
+ from fastapi import HTTPException
4
5
 
5
6
  from stac_fastapi.types.rfc3339 import (
6
7
  now_in_utc,
@@ -86,8 +87,11 @@ def test_parse_valid_str_to_datetime(test_input):
86
87
 
87
88
  @pytest.mark.parametrize("test_input", invalid_intervals)
88
89
  def test_parse_invalid_interval_to_datetime(test_input):
89
- with pytest.raises(ValueError):
90
+ with pytest.raises(HTTPException) as exc_info:
90
91
  str_to_interval(test_input)
92
+ assert (
93
+ exc_info.value.status_code == 400
94
+ ), "Should return a 400 status code for invalid intervals"
91
95
 
92
96
 
93
97
  @pytest.mark.parametrize("test_input", valid_intervals)
@@ -1,104 +0,0 @@
1
- """rfc3339."""
2
- import re
3
- from datetime import datetime, timezone
4
- from typing import Optional, Tuple, Union
5
-
6
- import iso8601
7
- from pystac.utils import datetime_to_str
8
-
9
- RFC33339_PATTERN = (
10
- r"^(\d\d\d\d)\-(\d\d)\-(\d\d)(T|t)(\d\d):(\d\d):(\d\d)([.]\d+)?"
11
- r"(Z|([-+])(\d\d):(\d\d))$"
12
- )
13
-
14
- DateTimeType = Union[
15
- datetime,
16
- Tuple[datetime, datetime],
17
- Tuple[datetime, None],
18
- Tuple[None, datetime],
19
- ]
20
-
21
-
22
- def rfc3339_str_to_datetime(s: str) -> datetime:
23
- """Convert a string conforming to RFC 3339 to a :class:`datetime.datetime`.
24
-
25
- Uses :meth:`iso8601.parse_date` under the hood.
26
-
27
- Args:
28
- s (str) : The string to convert to :class:`datetime.datetime`.
29
-
30
- Returns:
31
- str: The datetime represented by the ISO8601 (RFC 3339) formatted string.
32
-
33
- Raises:
34
- ValueError: If the string is not a valid RFC 3339 string.
35
- """
36
- # Uppercase the string
37
- s = s.upper()
38
-
39
- # Match against RFC3339 regex.
40
- result = re.match(RFC33339_PATTERN, s)
41
- if not result:
42
- raise ValueError("Invalid RFC3339 datetime.")
43
-
44
- # Parse with pyiso8601
45
- return iso8601.parse_date(s)
46
-
47
-
48
- def str_to_interval(interval: Optional[str]) -> Optional[DateTimeType]:
49
- """Extract a tuple of datetimes from an interval string.
50
-
51
- Interval strings are defined by
52
- OGC API - Features Part 1 for the datetime query parameter value. These follow the
53
- form '1985-04-12T23:20:50.52Z/1986-04-12T23:20:50.52Z', and allow either the start
54
- or end (but not both) to be open-ended with '..' or ''.
55
-
56
- Args:
57
- interval (str or None): The interval string to convert to a tuple of
58
- datetime.datetime objects, or None if no datetime is specified.
59
-
60
- Returns:
61
- Optional[DateTimeType]: A tuple of datetime.datetime objects or None if
62
- input is None.
63
-
64
- Raises:
65
- ValueError: If the string is not a valid interval string and not None.
66
- """
67
- if interval is None:
68
- return None
69
-
70
- if not interval:
71
- raise ValueError("Empty interval string is invalid.")
72
-
73
- values = interval.split("/")
74
- if len(values) == 1:
75
- # Single date for == date case
76
- return rfc3339_str_to_datetime(values[0])
77
- elif len(values) > 2:
78
- raise ValueError(
79
- f"Interval string '{interval}' contains more than one forward slash."
80
- )
81
-
82
- start = None
83
- end = None
84
- if values[0] not in ["..", ""]:
85
- start = rfc3339_str_to_datetime(values[0])
86
- if values[1] not in ["..", ""]:
87
- end = rfc3339_str_to_datetime(values[1])
88
-
89
- if start is None and end is None:
90
- raise ValueError("Double open-ended intervals are not allowed.")
91
- if start is not None and end is not None and start > end:
92
- raise ValueError("Start datetime cannot be before end datetime.")
93
- else:
94
- return start, end
95
-
96
-
97
- def now_in_utc() -> datetime:
98
- """Return a datetime value of now with the UTC timezone applied."""
99
- return datetime.now(timezone.utc)
100
-
101
-
102
- def now_to_rfc3339_str() -> str:
103
- """Return an RFC 3339 string representing now."""
104
- return datetime_to_str(now_in_utc())