BESS-JPL 1.6.1__tar.gz → 1.7.0__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.

Potentially problematic release.


This version of BESS-JPL might be problematic. Click here for more details.

Files changed (75) hide show
  1. bess_jpl-1.6.1/BESS_JPL/BESS.py → bess_jpl-1.7.0/BESS_JPL/BESS_JPL.py +76 -24
  2. bess_jpl-1.7.0/BESS_JPL/version.txt +1 -0
  3. {bess_jpl-1.6.1 → bess_jpl-1.7.0/BESS_JPL.egg-info}/PKG-INFO +4 -2
  4. {bess_jpl-1.6.1 → bess_jpl-1.7.0}/BESS_JPL.egg-info/SOURCES.txt +2 -1
  5. {bess_jpl-1.6.1 → bess_jpl-1.7.0}/BESS_JPL.egg-info/requires.txt +3 -1
  6. {bess_jpl-1.6.1/BESS_JPL.egg-info → bess_jpl-1.7.0}/PKG-INFO +4 -2
  7. bess_jpl-1.7.0/Processing BESS with rasters with default parameters.ipynb +334 -0
  8. {bess_jpl-1.6.1 → bess_jpl-1.7.0}/Processing BESS with rasters.ipynb +61 -8635
  9. bess_jpl-1.7.0/processing_BESS_with_rasters_and_default_parameters.py +102 -0
  10. {bess_jpl-1.6.1 → bess_jpl-1.7.0}/pyproject.toml +5 -3
  11. bess_jpl-1.6.1/BESS_JPL/BESS_JPL.py +0 -23
  12. bess_jpl-1.6.1/BESS_JPL/version.txt +0 -1
  13. {bess_jpl-1.6.1 → bess_jpl-1.7.0}/.github/workflows/ci.yml +0 -0
  14. {bess_jpl-1.6.1 → bess_jpl-1.7.0}/.github/workflows/python-publish.yml +0 -0
  15. {bess_jpl-1.6.1 → bess_jpl-1.7.0}/.gitignore +0 -0
  16. {bess_jpl-1.6.1 → bess_jpl-1.7.0}/BESS_JPL/C3_photosynthesis.py +0 -0
  17. {bess_jpl-1.6.1 → bess_jpl-1.7.0}/BESS_JPL/C4_fraction.jpeg +0 -0
  18. {bess_jpl-1.6.1 → bess_jpl-1.7.0}/BESS_JPL/C4_fraction.tif +0 -0
  19. {bess_jpl-1.6.1 → bess_jpl-1.7.0}/BESS_JPL/C4_photosynthesis.py +0 -0
  20. {bess_jpl-1.6.1 → bess_jpl-1.7.0}/BESS_JPL/FVC_from_NDVI.py +0 -0
  21. {bess_jpl-1.6.1 → bess_jpl-1.7.0}/BESS_JPL/LAI_from_NDVI.py +0 -0
  22. {bess_jpl-1.6.1 → bess_jpl-1.7.0}/BESS_JPL/NDVI_maximum.jpeg +0 -0
  23. {bess_jpl-1.6.1 → bess_jpl-1.7.0}/BESS_JPL/NDVI_maximum.tif +0 -0
  24. {bess_jpl-1.6.1 → bess_jpl-1.7.0}/BESS_JPL/NDVI_minimum.jpeg +0 -0
  25. {bess_jpl-1.6.1 → bess_jpl-1.7.0}/BESS_JPL/NDVI_minimum.tif +0 -0
  26. {bess_jpl-1.6.1 → bess_jpl-1.7.0}/BESS_JPL/SZA/__init__.py +0 -0
  27. {bess_jpl-1.6.1 → bess_jpl-1.7.0}/BESS_JPL/SZA/daylight_hours.py +0 -0
  28. {bess_jpl-1.6.1 → bess_jpl-1.7.0}/BESS_JPL/__init__.py +0 -0
  29. {bess_jpl-1.6.1 → bess_jpl-1.7.0}/BESS_JPL/ball_berry_intercept_C3.jpeg +0 -0
  30. {bess_jpl-1.6.1 → bess_jpl-1.7.0}/BESS_JPL/ball_berry_intercept_C3.tif +0 -0
  31. {bess_jpl-1.6.1 → bess_jpl-1.7.0}/BESS_JPL/ball_berry_slope_C3.jpeg +0 -0
  32. {bess_jpl-1.6.1 → bess_jpl-1.7.0}/BESS_JPL/ball_berry_slope_C3.tif +0 -0
  33. {bess_jpl-1.6.1 → bess_jpl-1.7.0}/BESS_JPL/ball_berry_slope_C4.jpeg +0 -0
  34. {bess_jpl-1.6.1 → bess_jpl-1.7.0}/BESS_JPL/ball_berry_slope_C4.tif +0 -0
  35. {bess_jpl-1.6.1 → bess_jpl-1.7.0}/BESS_JPL/calculate_VCmax.py +0 -0
  36. {bess_jpl-1.6.1 → bess_jpl-1.7.0}/BESS_JPL/canopy_energy_balance.py +0 -0
  37. {bess_jpl-1.6.1 → bess_jpl-1.7.0}/BESS_JPL/canopy_longwave_radiation.py +0 -0
  38. {bess_jpl-1.6.1 → bess_jpl-1.7.0}/BESS_JPL/canopy_shortwave_radiation.py +0 -0
  39. {bess_jpl-1.6.1 → bess_jpl-1.7.0}/BESS_JPL/carbon_uptake_efficiency.jpeg +0 -0
  40. {bess_jpl-1.6.1 → bess_jpl-1.7.0}/BESS_JPL/carbon_uptake_efficiency.tif +0 -0
  41. {bess_jpl-1.6.1 → bess_jpl-1.7.0}/BESS_JPL/carbon_water_fluxes.py +0 -0
  42. {bess_jpl-1.6.1 → bess_jpl-1.7.0}/BESS_JPL/constants.py +0 -0
  43. {bess_jpl-1.6.1 → bess_jpl-1.7.0}/BESS_JPL/interpolate_C3_C4.py +0 -0
  44. {bess_jpl-1.6.1 → bess_jpl-1.7.0}/BESS_JPL/kn.jpeg +0 -0
  45. {bess_jpl-1.6.1 → bess_jpl-1.7.0}/BESS_JPL/kn.tif +0 -0
  46. {bess_jpl-1.6.1 → bess_jpl-1.7.0}/BESS_JPL/load_C4_fraction.py +0 -0
  47. {bess_jpl-1.6.1 → bess_jpl-1.7.0}/BESS_JPL/load_NDVI_maximum.py +0 -0
  48. {bess_jpl-1.6.1 → bess_jpl-1.7.0}/BESS_JPL/load_NDVI_minimum.py +0 -0
  49. {bess_jpl-1.6.1 → bess_jpl-1.7.0}/BESS_JPL/load_ball_berry_intercept_C3.py +0 -0
  50. {bess_jpl-1.6.1 → bess_jpl-1.7.0}/BESS_JPL/load_ball_berry_slope_C3.py +0 -0
  51. {bess_jpl-1.6.1 → bess_jpl-1.7.0}/BESS_JPL/load_ball_berry_slope_C4.py +0 -0
  52. {bess_jpl-1.6.1 → bess_jpl-1.7.0}/BESS_JPL/load_carbon_uptake_efficiency.py +0 -0
  53. {bess_jpl-1.6.1 → bess_jpl-1.7.0}/BESS_JPL/load_kn.py +0 -0
  54. {bess_jpl-1.6.1 → bess_jpl-1.7.0}/BESS_JPL/load_peakVCmax_C3.py +0 -0
  55. {bess_jpl-1.6.1 → bess_jpl-1.7.0}/BESS_JPL/load_peakVCmax_C4.py +0 -0
  56. {bess_jpl-1.6.1 → bess_jpl-1.7.0}/BESS_JPL/meteorology.py +0 -0
  57. {bess_jpl-1.6.1 → bess_jpl-1.7.0}/BESS_JPL/peakVCmax_C3.jpeg +0 -0
  58. {bess_jpl-1.6.1 → bess_jpl-1.7.0}/BESS_JPL/peakVCmax_C3.tif +0 -0
  59. {bess_jpl-1.6.1 → bess_jpl-1.7.0}/BESS_JPL/peakVCmax_C4.jpeg +0 -0
  60. {bess_jpl-1.6.1 → bess_jpl-1.7.0}/BESS_JPL/peakVCmax_C4.tif +0 -0
  61. {bess_jpl-1.6.1 → bess_jpl-1.7.0}/BESS_JPL/soil_energy_balance.py +0 -0
  62. {bess_jpl-1.6.1 → bess_jpl-1.7.0}/BESS_JPL/vegetation_conversion/__init__.py +0 -0
  63. {bess_jpl-1.6.1 → bess_jpl-1.7.0}/BESS_JPL/vegetation_conversion/vegetation_conversion.py +0 -0
  64. {bess_jpl-1.6.1 → bess_jpl-1.7.0}/BESS_JPL.egg-info/dependency_links.txt +0 -0
  65. {bess_jpl-1.6.1 → bess_jpl-1.7.0}/BESS_JPL.egg-info/top_level.txt +0 -0
  66. {bess_jpl-1.6.1 → bess_jpl-1.7.0}/C4 Fraction.ipynb +0 -0
  67. {bess_jpl-1.6.1 → bess_jpl-1.7.0}/Dockerfile +0 -0
  68. {bess_jpl-1.6.1 → bess_jpl-1.7.0}/LICENSE +0 -0
  69. {bess_jpl-1.6.1 → bess_jpl-1.7.0}/Li_2023_RSE.pdf +0 -0
  70. {bess_jpl-1.6.1 → bess_jpl-1.7.0}/MANIFEST.in +0 -0
  71. {bess_jpl-1.6.1 → bess_jpl-1.7.0}/README.md +0 -0
  72. {bess_jpl-1.6.1 → bess_jpl-1.7.0}/makefile +0 -0
  73. {bess_jpl-1.6.1 → bess_jpl-1.7.0}/setup.cfg +0 -0
  74. {bess_jpl-1.6.1 → bess_jpl-1.7.0}/tests/test_import_breathing_earth_system_simulator.py +0 -0
  75. {bess_jpl-1.6.1 → bess_jpl-1.7.0}/tests/test_import_dependencies.py +0 -0
@@ -9,38 +9,46 @@ from rasters import Raster, RasterGeometry
9
9
  from check_distribution import check_distribution
10
10
 
11
11
  from sun_angles import calculate_SZA_from_DOY_and_hour
12
+ from solar_apparent_time import solar_day_of_year_for_area, solar_hour_of_day_for_area
12
13
 
13
14
  from koppengeiger import load_koppen_geiger
14
15
  from gedi_canopy_height import load_canopy_height
15
16
  from FLiESANN import FLiESANN
16
17
  from GEOS5FP import GEOS5FP
17
18
  from MODISCI import MODISCI
19
+ from NASADEM import NASADEM
18
20
 
19
21
  from .constants import *
20
- from .vegetation_conversion import LAI_from_NDVI
21
- from .canopy_shortwave_radiation import canopy_shortwave_radiation
22
- from .carbon_water_fluxes import carbon_water_fluxes
23
- from .meteorology import meteorology, SVP_Pa_from_Ta_K
24
- from .interpolate_C3_C4 import interpolate_C3_C4
25
- from .calculate_VCmax import calculate_VCmax
26
- from .load_NDVI_minimum import load_NDVI_minimum
27
- from .load_NDVI_maximum import load_NDVI_maximum
28
- from .load_C4_fraction import load_C4_fraction
29
- from .load_carbon_uptake_efficiency import load_carbon_uptake_efficiency
30
- from .load_kn import load_kn
31
- from .load_peakVCmax_C3 import load_peakVCmax_C3
32
- from .load_peakVCmax_C4 import load_peakVCmax_C4
33
- from .load_ball_berry_intercept_C3 import load_ball_berry_intercept_C3
34
- from .load_ball_berry_slope_C3 import load_ball_berry_slope_C3
35
- from .load_ball_berry_slope_C4 import load_ball_berry_slope_C4
22
+ from .C3_photosynthesis import *
23
+ from .C4_photosynthesis import *
24
+ from .canopy_energy_balance import *
25
+ from .canopy_longwave_radiation import *
26
+ from .canopy_shortwave_radiation import *
27
+ from .carbon_water_fluxes import *
28
+ from .FVC_from_NDVI import *
29
+ from .interpolate_C3_C4 import *
30
+ from .LAI_from_NDVI import *
31
+ from .load_C4_fraction import *
32
+ from .load_carbon_uptake_efficiency import *
33
+ from .load_kn import *
34
+ from .load_NDVI_minimum import *
35
+ from .load_NDVI_maximum import *
36
+ from .load_peakVCmax_C3 import *
37
+ from .load_peakVCmax_C4 import *
38
+ from .load_ball_berry_intercept_C3 import *
39
+ from .load_ball_berry_slope_C3 import *
40
+ from .load_ball_berry_slope_C4 import *
41
+ from .calculate_VCmax import *
42
+ from .meteorology import *
43
+ from .soil_energy_balance import *
36
44
 
37
45
  logger = logging.getLogger(__name__)
38
46
 
39
- def BESS(
47
+ def BESS_JPL(
40
48
  ST_C: Union[Raster, np.ndarray], # surface temperature in Celsius
41
49
  NDVI: Union[Raster, np.ndarray], # NDVI
42
50
  albedo: Union[Raster, np.ndarray], # surface albedo
43
- elevation_km: Union[Raster, np.ndarray], # elevation in kilometers
51
+ elevation_km: Union[Raster, np.ndarray] = None, # elevation in kilometers
44
52
  geometry: RasterGeometry = None,
45
53
  time_UTC: datetime = None,
46
54
  hour_of_day: np.ndarray = None,
@@ -83,6 +91,9 @@ def BESS(
83
91
  if geometry is None and isinstance(ST_C, Raster):
84
92
  geometry = ST_C.geometry
85
93
 
94
+ if GEOS5FP_connection is None:
95
+ GEOS5FP_connection = GEOS5FP()
96
+
86
97
  if (day_of_year is None or hour_of_day is None) and time_UTC is not None and geometry is not None:
87
98
  day_of_year = solar_day_of_year_for_area(time_UTC=time_UTC, geometry=geometry)
88
99
  hour_of_day = solar_hour_of_day_for_area(time_UTC=time_UTC, geometry=geometry)
@@ -90,6 +101,9 @@ def BESS(
90
101
  if time_UTC is None and day_of_year is None and hour_of_day is None:
91
102
  raise ValueError("no time given between time_UTC, day_of_year, and hour_of_day")
92
103
 
104
+ if elevation_km is None and geometry is not None:
105
+ elevation_km = NASADEM.elevation_km(geometry=geometry)
106
+
93
107
  # load air temperature in Celsius if not provided
94
108
  if Ta_C is None:
95
109
  Ta_C = GEOS5FP_connection.Ta_C(time_UTC=time_UTC, geometry=geometry, resampling=resampling)
@@ -138,6 +152,31 @@ def BESS(
138
152
  if ball_berry_intercept_C3 is None:
139
153
  ball_berry_intercept_C3 = load_ball_berry_intercept_C3(geometry=geometry, resampling=resampling)
140
154
 
155
+ # Create a dictionary of variables to check
156
+ variables_to_check = {
157
+ "Rg": Rg,
158
+ "VISdiff": VISdiff,
159
+ "VISdir": VISdir,
160
+ "NIRdiff": NIRdiff,
161
+ "NIRdir": NIRdir,
162
+ "UV": UV,
163
+ "albedo_visible": albedo_visible,
164
+ "albedo_NIR": albedo_NIR
165
+ }
166
+
167
+ # Check for None values and size mismatches
168
+ reference_size = None
169
+ for name, var in variables_to_check.items():
170
+ if var is None:
171
+ logger.warning(f"Variable '{name}' is None.")
172
+ else:
173
+ # Get the size of the variable if it's a numpy array
174
+ size = var.shape if isinstance(var, np.ndarray) else None
175
+ if reference_size is None:
176
+ reference_size = size # Set the first non-None size as the reference
177
+ elif size != reference_size:
178
+ logger.warning(f"Variable '{name}' has a different size: {size} (expected: {reference_size}).")
179
+
141
180
  # check if any of the FLiES outputs are not given
142
181
  if None in (Rg, VISdiff, VISdir, NIRdiff, NIRdir, UV, albedo_visible, albedo_NIR):
143
182
  # load cloud optical thickness if not provided
@@ -152,8 +191,10 @@ def BESS(
152
191
 
153
192
  # run FLiES radiative transfer model
154
193
  FLiES_results = FLiESANN(
194
+ time_UTC=time_UTC,
155
195
  day_of_year=day_of_year,
156
196
  hour_of_day=hour_of_day,
197
+ geometry=geometry,
157
198
  albedo=albedo,
158
199
  COT=COT,
159
200
  AOT=AOT,
@@ -162,7 +203,6 @@ def BESS(
162
203
  elevation_km=elevation_km,
163
204
  SZA=SZA,
164
205
  KG_climate=KG_climate,
165
- geometry=geometry,
166
206
  GEOS5FP_connection=GEOS5FP_connection
167
207
  )
168
208
 
@@ -173,8 +213,19 @@ def BESS(
173
213
  NIRdiff = FLiES_results["NIRdiff"]
174
214
  NIRdir = FLiES_results["NIRdir"]
175
215
  UV = FLiES_results["UV"]
176
- albedo_visible = FLiES_results["VIS"]
177
- albedo_NIR = FLiES_results["NIR"]
216
+ # albedo_visible = FLiES_results["VIS"]
217
+ # albedo_NIR = FLiES_results["NIR"]
218
+ albedo_NWP = GEOS5FP_connection.ALBEDO(time_UTC=time_UTC, geometry=geometry, resampling=resampling)
219
+ RVIS_NWP = GEOS5FP_connection.ALBVISDR(time_UTC=time_UTC, geometry=geometry, resampling=resampling)
220
+ albedo_visible = rt.clip(albedo * (RVIS_NWP / albedo_NWP), 0, 1)
221
+ check_distribution(albedo_visible, "RVIS")
222
+ RNIR_NWP = GEOS5FP_connection.ALBNIRDR(time_UTC=time_UTC, geometry=geometry, resampling=resampling)
223
+ albedo_NIR = rt.clip(albedo * (RNIR_NWP / albedo_NWP), 0, 1)
224
+ check_distribution(albedo_NIR, "RNIR")
225
+ PARDir = VISdir
226
+ check_distribution(PARDir, "PARDir")
227
+ else:
228
+ logger.info("using given FLiES output as BESS parameters")
178
229
 
179
230
  # load koppen geiger climate classification if not provided
180
231
  if KG_climate is None:
@@ -210,7 +261,7 @@ def BESS(
210
261
 
211
262
  # calculate solar zenith angle if not provided
212
263
  if SZA is None:
213
- SZA = calculate_SZA_from_DOY_and_hour(lat, lon, day_of_year, hour_of_day)
264
+ SZA = calculate_SZA_from_DOY_and_hour(geometry.lat, geometry.lon, day_of_year, hour_of_day)
214
265
 
215
266
  if CI is None and geometry is not None:
216
267
  modisci = MODISCI()
@@ -323,7 +374,6 @@ def BESS(
323
374
  for var_name, var_value in canopy_radiation_outputs.items():
324
375
  check_distribution(var_value, var_name, time_UTC)
325
376
 
326
-
327
377
  canopy_temperature_K = canopy_temperature_C + 273.15
328
378
  soil_temperature_K = soil_temperature_C + 273.15
329
379
 
@@ -376,7 +426,6 @@ def BESS(
376
426
  for var_name, var_value in carbon_water_fluxes_outputs.items():
377
427
  check_distribution(var_value, var_name, time_UTC)
378
428
 
379
-
380
429
  GPP_C4, LE_C4, LE_soil_C4, LE_canopy_C4, Rn_C4, Rn_soil_C4, Rn_canopy_C4 = carbon_water_fluxes(
381
430
  canopy_temperature_K=canopy_temperature_K, # canopy temperature in Kelvin
382
431
  soil_temperature_K=soil_temperature_K, # soil temperature in Kelvin
@@ -431,6 +480,9 @@ def BESS(
431
480
  GPP = np.clip(interpolate_C3_C4(GPP_C3, GPP_C4, C4_fraction), 0, 50)
432
481
  GPP = np.where(np.isnan(ST_K), np.nan, GPP)
433
482
 
483
+ if isinstance(geometry, RasterGeometry):
484
+ GPP = Raster(GPP, geometry=geometry)
485
+
434
486
  # upscale from instantaneous to daily
435
487
 
436
488
  # upscale GPP to daily
@@ -0,0 +1 @@
1
+ 1.7.0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: BESS-JPL
3
- Version: 1.6.1
3
+ Version: 1.7.0
4
4
  Summary: Breathing Earth System Simulator (BESS) Gross Primary Production (GPP) and Evapotranspiration (ET) Model Python
5
5
  Author-email: Gregory Halverson <gregory.h.halverson@jpl.nasa.gov>
6
6
  Project-URL: Homepage, https://github.com/JPL-Evapotranspiration-Algorithms/BESS-JPL
@@ -10,13 +10,15 @@ Requires-Python: >=3.10
10
10
  Description-Content-Type: text/markdown
11
11
  License-File: LICENSE
12
12
  Requires-Dist: check-distribution
13
- Requires-Dist: FLiESANN>=1.4.1
13
+ Requires-Dist: FLiESANN>=1.4.2
14
14
  Requires-Dist: gedi-canopy-height>=1.1.0
15
15
  Requires-Dist: GEOS5FP>=1.1.1
16
16
  Requires-Dist: koppengeiger>=1.0.4
17
17
  Requires-Dist: MODISCI>=1.3.0
18
+ Requires-Dist: NASADEM
18
19
  Requires-Dist: numpy
19
20
  Requires-Dist: rasters
21
+ Requires-Dist: solar-apparent-time>=1.3.2
20
22
  Provides-Extra: dev
21
23
  Requires-Dist: build; extra == "dev"
22
24
  Requires-Dist: pytest>=6.0; extra == "dev"
@@ -4,13 +4,14 @@ Dockerfile
4
4
  LICENSE
5
5
  Li_2023_RSE.pdf
6
6
  MANIFEST.in
7
+ Processing BESS with rasters with default parameters.ipynb
7
8
  Processing BESS with rasters.ipynb
8
9
  README.md
9
10
  makefile
11
+ processing_BESS_with_rasters_and_default_parameters.py
10
12
  pyproject.toml
11
13
  .github/workflows/ci.yml
12
14
  .github/workflows/python-publish.yml
13
- BESS_JPL/BESS.py
14
15
  BESS_JPL/BESS_JPL.py
15
16
  BESS_JPL/C3_photosynthesis.py
16
17
  BESS_JPL/C4_fraction.jpeg
@@ -1,11 +1,13 @@
1
1
  check-distribution
2
- FLiESANN>=1.4.1
2
+ FLiESANN>=1.4.2
3
3
  gedi-canopy-height>=1.1.0
4
4
  GEOS5FP>=1.1.1
5
5
  koppengeiger>=1.0.4
6
6
  MODISCI>=1.3.0
7
+ NASADEM
7
8
  numpy
8
9
  rasters
10
+ solar-apparent-time>=1.3.2
9
11
 
10
12
  [dev]
11
13
  build
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: BESS-JPL
3
- Version: 1.6.1
3
+ Version: 1.7.0
4
4
  Summary: Breathing Earth System Simulator (BESS) Gross Primary Production (GPP) and Evapotranspiration (ET) Model Python
5
5
  Author-email: Gregory Halverson <gregory.h.halverson@jpl.nasa.gov>
6
6
  Project-URL: Homepage, https://github.com/JPL-Evapotranspiration-Algorithms/BESS-JPL
@@ -10,13 +10,15 @@ Requires-Python: >=3.10
10
10
  Description-Content-Type: text/markdown
11
11
  License-File: LICENSE
12
12
  Requires-Dist: check-distribution
13
- Requires-Dist: FLiESANN>=1.4.1
13
+ Requires-Dist: FLiESANN>=1.4.2
14
14
  Requires-Dist: gedi-canopy-height>=1.1.0
15
15
  Requires-Dist: GEOS5FP>=1.1.1
16
16
  Requires-Dist: koppengeiger>=1.0.4
17
17
  Requires-Dist: MODISCI>=1.3.0
18
+ Requires-Dist: NASADEM
18
19
  Requires-Dist: numpy
19
20
  Requires-Dist: rasters
21
+ Requires-Dist: solar-apparent-time>=1.3.2
20
22
  Provides-Extra: dev
21
23
  Requires-Dist: build; extra == "dev"
22
24
  Requires-Dist: pytest>=6.0; extra == "dev"