Releases: NarenKarthikBM/python-cdo-wrapper
Release list
V1.1.2 - Griddes parser bug fix
Release Notes: v1.1.2
🎯 Feature Enhancement Release
This release significantly enhances the GriddesParser to support all CDO grid types, including rotated pole projections, Gaussian grids, and unstructured meshes, with intelligent fallback handling for unknown attributes.
✨ What's New
Enhanced GriddesParser - Universal Grid Type Support
The Enhancement:
Version 1.1.2 expands GriddesParser from basic lonlat grids to comprehensive support for all 7 major CDO grid types, including advanced projections and irregular meshes.
What's Supported Now:
| Grid Type | Description | New in v1.1.2 |
|---|---|---|
| lonlat | Regular latitude-longitude grids | ✅ Enhanced |
| gaussian | Gaussian grids with regular longitude spacing | ✨ NEW |
| gaussian_reduced | Reduced Gaussian grids with variable longitude points | ✨ NEW |
| generic | Generic grids with minimal metadata | ✨ NEW |
| projection | Rotated pole and CF Conventions projections | ✨ NEW |
| curvilinear | 2D coordinate arrays for structured grids | ✨ NEW |
| unstructured | Irregular meshes and point clouds | ✨ NEW |
🚀 Key Features
1. Rotated Pole Projection Support (CF Conventions)
Full support for rotated latitude-longitude grids commonly used in regional climate models (RCMs):
from python_cdo_wrapper import CDO
cdo = CDO()
result = cdo.griddes("rotated_grid_data.nc")
grid = result.primary_grid
# Check if grid is rotated
if grid.is_rotated:
print(f"Rotated pole projection detected!")
print(f"North pole longitude: {grid.grid_north_pole_longitude}°")
print(f"North pole latitude: {grid.grid_north_pole_latitude}°")
print(f"Grid mapping: {grid.grid_mapping_name}")
# Output:
# Rotated pole projection detected!
# North pole longitude: -123.34°
# North pole latitude: 79.95°
# Grid mapping: rotated_latitude_longitudeNew GridInfo Fields:
grid_mapping- Grid mapping variable namegrid_mapping_name- CF Conventions projection namegrid_north_pole_longitude- Rotated pole longitudegrid_north_pole_latitude- Rotated pole latitude
2. Gaussian Grid Support
Support for both regular and reduced Gaussian grids used in global models:
# Regular Gaussian grid
result = cdo.griddes("gaussian_data.nc")
grid = result.primary_grid
if grid.is_gaussian:
print(f"Gaussian grid: {grid.gridtype}")
print(f"Grid size: {grid.gridsize} points")
print(f"Truncation (np): {grid.np}")
print(f"Dimensions: {grid.xsize} x {grid.ysize}")
# Reduced Gaussian grid
result = cdo.griddes("reduced_gaussian_data.nc")
grid = result.primary_grid
if grid.gridtype == "gaussian_reduced":
print(f"Reduced Gaussian grid")
print(f"Variable longitude points per latitude row")
if grid.rowlon:
print(f"Longitude points: {grid.rowlon[:5]}...") # First 5 rowsNew GridInfo Fields:
np- Gaussian grid truncation parameterrowlon- Number of longitude points per latitude row (reduced Gaussian)
3. Unstructured Grid Support
Support for irregular meshes and point clouds:
result = cdo.griddes("unstructured_mesh.nc")
grid = result.primary_grid
if grid.is_unstructured:
print(f"Unstructured grid detected")
print(f"Total points: {grid.points}")
print(f"Vertices per cell: {grid.nvertex}")
print(f"Grid size: {grid.gridsize}")New GridInfo Fields:
points- Number of points in unstructured gridnvertex- Number of vertices per cell
4. Intelligent Fallback Mechanism
The Problem:
CDO grids may have custom or unrecognized attributes from specialized models or future CDO versions.
The Solution:
Unknown attributes are automatically stored in raw_attributes dictionary for inspection:
result = cdo.griddes("custom_grid.nc")
grid = result.primary_grid
# Known attributes parsed normally
print(f"Grid type: {grid.gridtype}")
print(f"Size: {grid.xsize} x {grid.ysize}")
# Unknown attributes stored for inspection
if grid.raw_attributes:
print(f"\nUnknown attributes found:")
for key, value in grid.raw_attributes.items():
print(f" {key}: {value}")
# Output:
# Grid type: lonlat
# Size: 50 x 20
#
# Unknown attributes found:
# custom_attr1: special_value
# custom_number: 42
# custom_float: 3.14159Benefits:
- ✅ Never fails on unknown attributes
- ✅ Preserves all information for debugging
- ✅ Future-proof for new CDO features
- ✅ Enables inspection of custom grid metadata
5. Comprehensive GridInfo Properties
Eight new convenience properties for grid type checking:
result = cdo.griddes("data.nc")
grid = result.primary_grid
# Grid type classification
print(f"Regular grid: {grid.is_regular}") # lonlat, generic, projection
print(f"Gaussian grid: {grid.is_gaussian}") # gaussian, gaussian_reduced
print(f"Structured grid: {grid.is_structured}") # has regular dimensions
print(f"Unstructured grid: {grid.is_unstructured}") # irregular mesh
# Projection information
print(f"Rotated projection: {grid.is_rotated}") # rotated pole
print(f"Has projection: {grid.has_projection}") # any projection info
# Spatial extent (if available)
if grid.lon_range:
print(f"Longitude range: {grid.lon_range[0]}° to {grid.lon_range[1]}°")
if grid.lat_range:
print(f"Latitude range: {grid.lat_range[0]}° to {grid.lat_range[1]}°")🎯 Use Cases
Regional Climate Model Analysis
from python_cdo_wrapper import CDO
cdo = CDO()
# Analyze RCM grid with rotated pole projection
result = cdo.griddes("EUR-11_rotated.nc")
grid = result.primary_grid
if grid.is_rotated:
print(f"European domain with rotated pole")
print(f"Rotated coordinates: {grid.xname}/{grid.yname}")
print(f"Resolution: {grid.xinc}° x {grid.yinc}°")
print(f"Domain size: {grid.xsize} x {grid.ysize}")
# Coordinate transformation information
print(f"\nProjection details:")
print(f" Mapping: {grid.grid_mapping_name}")
print(f" Pole: ({grid.grid_north_pole_longitude}°E, "
f"{grid.grid_north_pole_latitude}°N)")Global Model Comparison
# Compare different global model grids
models = ["ERA5_gaussian.nc", "CMIP6_lonlat.nc", "IFS_reduced.nc"]
for model_file in models:
result = cdo.griddes(model_file)
grid = result.primary_grid
print(f"\n{model_file}:")
print(f" Type: {grid.gridtype}")
print(f" Size: {grid.gridsize} points")
if grid.is_gaussian:
print(f" Gaussian truncation: N{grid.np}")
elif grid.is_regular:
print(f" Resolution: {grid.xinc}° x {grid.yinc}°")Unstructured Mesh Inspection
# Analyze unstructured mesh from ocean model
result = cdo.griddes("ocean_mesh.nc")
grid = result.primary_grid
if grid.is_unstructured:
print(f"Ocean mesh analysis:")
print(f" Total mesh points: {grid.points:,}")
print(f" Cell structure: {grid.nvertex}-vertex cells")
print(f" Grid efficiency: {grid.gridsize / grid.points:.2%}")🧪 Testing
Comprehensive Test Coverage
Added 13 new test cases covering all grid types and edge cases:
Grid Type Tests:
- ✅ Regular lonlat grids
- ✅ Rotated pole projection grids
- ✅ Gaussian grids (regular)
- ✅ Reduced Gaussian grids
- ✅ Generic grids
- ✅ Curvilinear grids
- ✅ Unstructured grids
Feature Tests:
8. ✅ Projection parameter parsing
9. ✅ GridInfo convenience properties (lonlat)
10. ✅ GridInfo convenience properties (rotated)
11. ✅ Unknown attribute fallback
12. ✅ Type-safe attribute parsing
13. ✅ Error handling for malformed output
Test Files:
tests/test_parsers/test_grid.py- Unit tests with sample CDO outputs- Sample outputs for 7 different grid types included as fixtures
All tests pass on:
- ✅ Ubuntu (Python 3.9, 3.10, 3.11, 3.12)
- ✅ macOS (Python 3.9, 3.10, 3.11, 3.12)
💥 Breaking Changes
None - This is a backward-compatible enhancement. All existing code continues to work as before.
New Features:
- Existing
GridInfofields remain unchanged - New optional fields with default
Nonevalues - New convenience properties are additive
raw_attributesfield is optional
🔧 Technical Details
Type-Safe Attribute Parsing
New _parse_grid_attribute() method handles automatic type detection:
# Integer fields
gridsize, xsize, ysize, np, points, nvertex → int
# Float fields
xfirst, xinc, yfirst, yinc, scanningMode,
grid_north_pole_longitude, grid_north_pole_latitude → float
# Float list fields (space-separated)
xvals, yvals, levels → list[float]
# Integer list fields (space-separated)
rowlon → list[int]
# String fields (with quote stripping)
gridtype, xname, xlongname, xunits, etc. → strRobust Parsing:
- ✅ Automatic type conversion based on field name
- ✅ Graceful handling of parsing failures (fallback to raw string)
- ✅ Preservation of all data (unknown → raw_attributes)
- ✅ No data loss on parse errors
Known vs Unknown Attributes
The parser distinguishes between:
Known Attributes (mapped to GridInfo fields):
- Standard grid attributes: gridtype, gridsize, xsize, ysize, etc.
- Coordinate attributes: xfirst, xinc, xvals, etc.
- Projection attributes: grid_mapping, grid_north_pole_*, etc.
- Special attributes: np, rowlon, points, nvertex, etc.
Unknown Attributes (stored in raw_attributes):
- Custom model-specific attributes
- Future CDO attributes not yet supported
- Experimental or non-standard fields
This ensures forward compatibility with future CDO versions.
...
v1.1.1 - BinaryOPQuery execution bug fix
Release Notes: v1.1.1
🐛 Bug Fix Release
This is a critical bug fix release that corrects the command generation for binary operations in CDO queries.
🔧 What's Fixed
Binary Operation Command Generation
The Issue:
Version 1.1.0 incorrectly used bracket notation [ ... ] for binary operations, which is not valid CDO syntax for binary operators. This caused "too many inputs" errors when using nested binary operations like ifthen inside sub.
The Fix:
Binary operators (add, sub, mul, div, min, max) now use CDO's operator chaining syntax instead of brackets.
Command Generation - Before vs After:
| Scenario | v1.1.0 (Incorrect) | v1.1.1 (Correct) |
|---|---|---|
| Left has operators | cdo -sub [ -yearmean file1 ] file2 ❌ |
cdo -sub -yearmean file1 file2 ✅ |
| Both have operators | cdo -sub [ -yearmean file1 ] [ -timmean file2 ] ❌ |
cdo -sub -yearmean file1 -timmean file2 ✅ |
| Nested binary ops | Failed with "too many inputs" ❌ | cdo -sub -ifthen mask.nc data.nc clim.nc ✅ |
Key Points:
- ✅ Binary operators use operator chaining (no brackets)
- ✅ Operators are applied to their respective input files left-to-right
- ✅ Supports nested binary operations (e.g.,
iftheninsidesub) - ✅ All operations execute in a single CDO command (no temporary files)
- 📝 Bracket notation is only for variadic operators (merge, cat) - not binary operators
💥 Breaking Changes
None - This is a bug fix release that makes the package work correctly. If you were using binary operations in v1.1.0, they may not have been working properly.
🎯 What's Working Now
Example 1: Simple Anomaly Calculation
from python_cdo_wrapper import CDO, F
cdo = CDO()
# Calculate anomalies - works correctly now!
anomaly = cdo.query("data.nc").sub(F("climatology.nc")).compute()
# Generates: cdo -sub data.nc climatology.ncExample 2: Operations on Both Sides
# With operators on both operands - single command!
result = (
cdo.query("data.nc")
.select_var("tas")
.year_mean()
.sub(F("climatology.nc").time_mean())
.compute()
)
# Generates: cdo -sub -yearmean -selname,tas data.nc -timmean climatology.ncExample 3: Nested Binary Operations (The Bug Case)
# The original issue: ifthen inside sub
masked_data = cdo.query("data.nc").ifthen(F("mask.nc"))
anomaly = masked_data.sub(F("climatology.nc")).compute()
# Generates: cdo -sub -ifthen mask.nc data.nc climatology.nc
# This now works correctly! ✅Example 4: Complex Multi-Step Operations
# Standardized anomaly: (data - mean) / std
std_anomaly = (
cdo.query("data.nc")
.sub(F("climatology.nc"))
.div(F("std_dev.nc"))
.compute()
)
# Generates: cdo -div -sub data.nc climatology.nc std_dev.nc🧪 Testing
New Integration Tests
Added 5 comprehensive integration tests that execute actual CDO commands with real NetCDF files:
- ✅ Binary operations with operators on left operand
- ✅ Binary operations with operators on both sides
- ✅ Nested binary operations (ifthen inside sub) - the original bug case
- ✅ Anomaly calculations (data - time_mean)
- ✅ Chained binary operations (sub then div)
All tests pass with flying colors on:
- ✅ Ubuntu (Python 3.9, 3.10, 3.11, 3.12)
- ✅ macOS (Python 3.9, 3.10, 3.11, 3.12)
📦 Installation & Upgrade
Upgrading from v1.1.0
# Upgrade to the bug fix release
pip install --upgrade python-cdo-wrapper
# Or with shapefile support
pip install --upgrade python-cdo-wrapper[shapefiles]New Installation
# Core package
pip install python-cdo-wrapper
# With shapefile support
pip install python-cdo-wrapper[shapefiles]Prerequisites
- Python >= 3.9
- CDO >= 1.9.8 (recommended: >= 2.0.0)
- Optional: geopandas >= 0.10.0, shapely >= 2.0.0 (for shapefile masking)
🔗 Technical Details
Why Operator Chaining?
CDO processes operators right-to-left and applies them to their respective input files. Binary operators (which take exactly 2 inputs) use operator chaining syntax, not bracket notation.
From the CDO Tutorial:
"Operators can be chained directly to their input files without brackets for binary operations."
CDO Tutorial: Combining Operators
Example Flow:
cdo -sub -yearmean file1.nc -timmean file2.nc output.ncCDO evaluation:
- Apply
-yearmeantofile1.nc→ result1 - Apply
-timmeantofile2.nc→ result2 - Apply
-subwith (result1, result2) → output
📝 Documentation Updates
All documentation has been updated to reflect the correct syntax:
- ✅ README.md - Updated binary operation examples
- ✅ CHANGELOG.md - Documented the fix with examples
- ✅ MIGRATION_GUIDE.md - Updated v1.0.0 examples
- ✅ .github/copilot-instructions.md - Updated all references
- ✅ examples/v1.0.0-demo.ipynb - Updated example outputs
🙏 Acknowledgments
Special thanks to:
- The CDO team for excellent documentation on operator chaining
🐛 Known Issues & Limitations
None in this release!
This bug fix resolves all known command generation issues with binary operations.
Future Improvements
- Consider adding more optimization hints for large-scale operations
- Expand documentation with more climate science use cases
- Add performance benchmarks for operator chaining
📊 Full Changelog
[v1.1.1] - 2025-12-17
Fixed
- BinaryOpQuery command generation: Corrected to use CDO's operator chaining syntax (removed incorrect bracket notation)
- Binary operators (sub, add, mul, div, etc.) now use standard operator chaining
- ❌ Old (incorrect):
cdo -sub [ -yearmean file1 ] [ file2 ] - ✅ New (correct):
cdo -sub -yearmean file1 -timmean file2 - Operators are applied directly to their respective input files from left to right
- Supports nested binary operations (e.g.,
iftheninsidesub) - All operations execute in single CDO command
- Bracket notation is NOT used for binary operators - only for variadic operators (merge, cat)
Testing
- Added 5 integration tests for binary operations with actual CDO execution
- All tests pass on Ubuntu and macOS with Python 3.9-3.12
Documentation
- Updated all documentation to reflect correct operator chaining syntax
- Added technical notes explaining CDO's evaluation model
- Updated examples in README, MIGRATION_GUIDE, and notebooks
For complete details, see CHANGELOG.md.
📞 Support & Feedback
Found a bug? Please open an issue on GitHub:
https://github.com/NarenKarthikBM/python-cdo-wrapper/issues
Have questions? Check out:
- Documentation: README.md
- Examples: examples/
- Migration Guide: MIGRATION_GUIDE.md
🔗 Resources
- GitHub Repository: https://github.com/NarenKarthikBM/python-cdo-wrapper
- PyPI Package: https://pypi.org/project/python-cdo-wrapper/
- CDO Documentation: https://code.mpimet.mpg.de/projects/cdo/
- CDO Tutorial (Operator Chaining): https://code.mpimet.mpg.de/projects/cdo/wiki/Tutorial#Combining-Operators
Thank you for using python-cdo-wrapper! 🎉
Note: This release fixes a critical bug in v1.1.0. All users on v1.1.0 are encouraged to upgrade immediately.
V1.1.0 - Added Shapefile mixin and fixed BinaryOPQuery bug
🎉 What's New
Shapefile Masking Support
The headline feature of v1.1.0 is comprehensive shapefile masking for NetCDF files. Users can now clip climate data to geographic regions defined by shapefiles in a single, chainable operation.
Key Highlights:
# One-liner regional masking
masked = cdo.query("global_data.nc").mask_by_shapefile("region.shp").compute()
# Chain with other CDO operators
result = (
cdo.query("daily_precip.nc")
.mask_by_shapefile("sahel.shp")
.year_mean()
.field_mean()
.compute()
)Features:
- ✅ Complete automated pipeline (load shapefile → create mask → apply → cleanup)
- ✅ Support for both 1D (regular) and 2D (curvilinear) grids
- ✅ Automatic CRS reprojection to WGS84
- ✅ Multi-polygon shapefile support
- ✅ Custom coordinate naming (
lat_name,lon_nameparameters) - ✅ Automatic temporary file cleanup
- ✅ Secure temp file handling (no race conditions)
Installation:
pip install python-cdo-wrapper[shapefiles]This optional dependency group includes geopandas>=0.10.0 and shapely>=2.0.0.
🔧 Technical Improvements
Enhanced Binary Operations
Fixed: Binary operators (add, sub, mul, div, min, max) no longer generate unnecessary brackets in CDO commands.
- Binary operators take exactly two inputs and CDO assigns them unambiguously right-to-left
- Commands are now cleaner:
cdo -sub -yearmean data.nc clim.nc - Previously generated:
cdo -sub [ -yearmean data.nc ] clim.nc - Follows CDO best practices and may improve performance
Security Enhancements
- Replaced unsafe
tempfile.mktemp()with securetempfile.mkstemp()in shapefile masking - Prevents race condition vulnerabilities when creating temporary mask files
- Follows Python security best practices
📋 Complete Feature List
New Operators
Shapefile Masking
mask_by_shapefile(shapefile_path, lat_name="lat", lon_name="lon")- Clip NetCDF to shapefile polygon extent
- Chainable with all other CDO operators
- Returns masked xarray.Dataset
Advanced Utilities
create_mask_from_shapefile(shapefile_path, reference_nc, lat_name, lon_name)- Create reusable binary mask files
- Useful for processing multiple files with same region
- Returns xarray.Dataset with mask variable
Information Operators (Query API)
All CDO information commands are now available as terminating query methods (from v1.0.1, included in this release):
Variable Information:
.showname()- List variable names.showcode()- List variable codes.showunit()- List variable units.showlevel()- List vertical levels
Time Information:
.showdate()- List timestamps.showtime()- List time values.ntime()- Count timesteps
Dataset Information:
.sinfo()- Structured file info (returnsSinfoResult).info()- General info (returnsInfoResult).vlist()- Variable list (returnsVlistResult).partab()- Parameter table (returnsPartabResult).nvar()- Count variables.nlevel()- Count levels
Grid Information:
.griddes()- Grid description (returnsGriddesResult).zaxisdes()- Z-axis description (returnsZaxisdesResult)
These methods execute immediately and return structured results, making them convenient for pipeline inspection.
🔄 Migration Guide
From v1.0.x to v1.1.0
No breaking changes - v1.1.0 is fully backward compatible with v1.0.x.
Optional: Adopt Shapefile Masking
If you currently mask data manually with separate mask files:
Before (v1.0.x):
# Manual masking with pre-created mask
result = cdo.query("data.nc").select_mask("region_mask.nc").compute()After (v1.1.0):
# Direct shapefile masking
result = cdo.query("data.nc").mask_by_shapefile("region.shp").compute()Optional: Install Shapefile Support
# If you need shapefile masking
pip install --upgrade python-cdo-wrapper[shapefiles]
# Or just core package (no shapefile support)
pip install --upgrade python-cdo-wrapper📦 Installation & Upgrade
New Installation
# Core package
pip install python-cdo-wrapper
# With shapefile support
pip install python-cdo-wrapper[shapefiles]
# All optional features
pip install python-cdo-wrapper[shapefiles,dev,test]Upgrading from v1.0.x
pip install --upgrade python-cdo-wrapper
# Or with shapefiles:
pip install --upgrade python-cdo-wrapper[shapefiles]Prerequisites
- Python >= 3.9
- CDO >= 1.9.8 (recommended: >= 2.0.0)
- For shapefile masking: geopandas >= 0.10.0, shapely >= 2.0.0
📚 Documentation
Updated Documentation
- README.md - Added comprehensive shapefile masking section
- CHANGELOG.md - Detailed changelog for v1.1.0
- examples/shapefile_masking_example.py - 7 usage scenarios
Key Examples
Example 1: Simple Regional Analysis
from python_cdo_wrapper import CDO
cdo = CDO()
# Analyze temperature over Amazon basin
regional_temp = (
cdo.query("global_temperature.nc")
.mask_by_shapefile("amazon_basin.shp")
.year_mean()
.field_mean()
.compute()
)Example 2: Multi-step Climate Analysis
# Calculate regional precipitation anomaly
anomaly = (
cdo.query("monthly_precip.nc")
.mask_by_shapefile("sahel.shp")
.sub(F("climatology.nc"))
.year_mean()
.compute()
)Example 3: Reusable Masks for Multiple Files
from python_cdo_wrapper import create_mask_from_shapefile
# Create mask once
mask_ds = create_mask_from_shapefile(
shapefile_path="europe.shp",
reference_nc="template.nc"
)
mask_ds.to_netcdf("europe_mask.nc")
# Reuse for multiple files
file1 = cdo.query("data1.nc").select_mask("europe_mask.nc").compute()
file2 = cdo.query("data2.nc").select_mask("europe_mask.nc").compute()⚡ Performance Notes
Shapefile Masking Performance
For small to medium grids (< 100k points): Masking is fast, typically < 1 second.
For large grids (e.g., global 0.5° = ~260k points): The point-in-polygon test loop can take several seconds.
Optimization strategies:
- Pre-create and reuse masks for multiple files with the same region
- Use coarser grids when high resolution isn't needed
- Consider CDO's native region selection (
.select_region()) for simple rectangular boxes
Example of mask reuse:
# Create mask once (may be slow for large grids)
mask_ds = create_mask_from_shapefile("region.shp", "data.nc")
mask_ds.to_netcdf("region_mask.nc")
# Reuse for many files (fast)
for file in file_list:
result = cdo.query(file).select_mask("region_mask.nc").year_mean().compute()🧪 Testing
Test Coverage
- 9 unit tests for shapefile masking (all passing)
- 4 integration tests (require CDO installation)
- Security test verifying secure temp file creation
- Cleanup test verifying automatic temp file deletion
Running Tests
# Install with test dependencies
pip install python-cdo-wrapper[test,shapefiles]
# Run shapefile masking tests
pytest tests/test_shapefile_masking.py -v
# Run all tests
pytest🐛 Known Issues & Limitations
Shapefile Masking
- Large grids: Point-in-polygon tests can be slow (see Performance Notes above)
- Complex polygons: Very complex multi-polygon shapefiles may take longer to process
- CRS requirements: Shapefile must be in geographic coordinates or reprojectable to WGS84
Workarounds
- For repeated analyses on the same region, create and reuse mask files
- For simple rectangular regions, use
.select_region()instead - For very large grids, consider downsampling before masking
🔗 Resources
- GitHub Repository: https://github.com/NarenKarthikBM/python-cdo-wrapper
- PyPI Package: https://pypi.org/project/python-cdo-wrapper/
- CDO Documentation: https://code.mpimet.mpg.de/projects/cdo/
- GeoPandas Documentation: https://geopandas.org/
🙏 Acknowledgments
Special thanks to:
- The CDO team for the excellent climate data operators
- The geopandas and shapely communities for robust geospatial tools
📝 Full Changelog
For complete details, see CHANGELOG.md.
v1.1.0 Summary
- ✅ Added shapefile masking with
mask_by_shapefile()operator - ✅ Fixed binary operation bracket generation
- ✅ Enhanced security with secure temp file creation
- ✅ Improved performance documentation
- ✅ Enhanced test coverage for temp file cleanup
- ✅ Updated documentation and examples
Questions or Issues? Please open an issue on GitHub: https://github.com/NarenKarthikBM/python-cdo-wrapper/issues
V1.0.1 - Added terminal info query functions
🎉 Release v1.0.1 - Information Operators as Query Terminators
This release brings a major enhancement to the Query API: all CDO information operators are now available as terminating methods directly on CDOQuery. Extract metadata from processed data without intermediate files, making workflows more intuitive and efficient.
✨ New Features
Information Operators as Terminating Query Methods
All 15 CDO information operators can now be called directly on query chains, executing immediately (like .compute()) but returning metadata instead of datasets:
Variable Information Methods:
.showname()- Get list of variable names.showcode()- Get list of variable codes.showunit()- Get list of variable units.showlevel()- Get list of vertical levels
Time Information Methods:
.showdate()- Get list of dates (YYYY-MM-DD format).showtime()- Get list of times (HH:MM:SS format).ntime()- Get number of timesteps (alias for.count())
Count Methods:
.nvar()- Get number of variables.nlevel()- Get number of vertical levels
Structured Dataset Information:
.sinfo()→SinfoResult- Comprehensive dataset summary.info()→InfoResult- Timestep-by-timestep statistics.vlist()→VlistResult- Variable list with metadata.partab()→PartabResult- Parameter table information
Grid Information:
.griddes()→GriddesResult- Grid description.zaxisdes()→ZaxisdesResult- Vertical axis description
Key Benefits
- No intermediate files needed when inspecting processed data
- Chain processing and metadata extraction in a single pipeline
- Consistent Query API pattern following Django ORM conventions
- Works with or without operators in the pipeline
📊 Example Usage
from python_cdo_wrapper import CDO
cdo = CDO()
# Get variable names after processing - no intermediate file!
vars = cdo.query("data.nc").year_mean().showname()
# Returns: ['tas', 'pr', 'psl']
# Get number of timesteps after selection
n = cdo.query("data.nc").select_year(2020).ntime()
# Returns: 12
# Get dates after complex processing
dates = (
cdo.query("data.nc")
.select_var("tas")
.select_year(2020, 2021)
.showdate()
)
# Returns: ['2020-01-01', '2020-02-01', ..., '2021-12-01']
# Get comprehensive dataset info after processing
```python
# Get structured dataset info after processing
info = cdo.query("data.nc").year_mean().sinfo()
print(f"Variables: {info.var_names}")
print(f"Time range: {info.time_range}")
# Get grid info after remapping
grid = cdo.query("data.nc").remap_bil("r180x90").griddes()
print(f"Grid type: {grid.grids[0].gridtype}")
print(f"Grid size: {grid.grids[0].xsize} x {grid.grids[0].ysize}")🔧 Improvements
-
Parser Enhancements: Improved
SinfoParserandPartabParserrobustness- Fixed variable name handling in
SinfoParser(sinfo only provides parameter IDs) - Enhanced regex patterns in
PartabParserfor better field extraction - Better error handling for optional fields and edge cases
- Fixed variable name handling in
-
Test Data Quality: Upgraded test fixtures to CF-1.8 compliant standards
- Changed
temperature→tas(standard CF variable name) - Added proper GRIB parameter codes (tas=167, pr=228, psl=151)
- Improved NetCDF encoding and coordinate attributes
- Changed
-
Type Safety: Added proper type casting with
typing.castfor better IDE support -
Documentation: Added comprehensive example script
examples/info_operators_example.py -
Agent Configuration: Updated GitHub Copilot agent tool definitions
Backward Compatibility
Fully backward compatible - all existing code continues to work without changes. The new methods are additions to the Query API and don't affect existing functionality.
Recommended migration (optional):
# Old way (still works)
cdo.showname("data.nc")
# New way (recommended for processed data)
cdo.query("data.nc").year_mean().showname() # No intermediate file!Installation
pip install --upgrade python-cdo-wrapperFull Changelog: v1.0.0...v1.0.1
V1.0.0 - Django-ORM style query chaining
What's Changed
- V1.0.0 major overhaul - Add integration and unit tests for CDO wrapper functionality by @NarenKarthikBM in #4
Full Changelog: V0.2.1...V1.0.0
v0.2.1 - Enhanced SinfoParser
🎉 Release v0.2.1 - Enhanced SinfoParser
This release significantly enhances the SinfoParser to provide comprehensive parsing of all CDO sinfo output sections, making it easier to programmatically access climate data metadata.
✨ New Features
Complete Sinfo Output Parsing
- Metadata Extraction: Parse file format, institute, source, and variable field headers
- Grid Coordinates: Full grid information with automatic spatial resolution calculation
- Extracts grid type, dimensions, and coordinate ranges
- Automatically calculates
lon_resolutionandlat_resolution(e.g., 0.25 degrees)
- Vertical Coordinates: Parse vertical axis information (surface, pressure, hybrid levels)
- Time Coordinates: Comprehensive temporal data extraction
- Total timesteps, reference time, units, and calendar
- Complete list of all timestep values
- Automatic temporal resolution calculation (detects hourly, 6-hourly, daily, monthly intervals)
- Handles both regular and irregular time series
Enhanced Variable Parsing
- Support for both modern sinfo format (Institut/Source/Steptype) and legacy format (Date/Time)
- Robust type conversion for numeric fields
- Graceful handling of malformed data
📊 Example Usage
from python_cdo_wrapper import CDO, parse_cdo_output
cdo = CDO()
output = cdo("sinfo", input="data.nc")
parsed = parse_cdo_output("sinfo", output)
# Access grid resolution
print(parsed["grid"]["lon_resolution"]) # 0.25
print(parsed["grid"]["lat_resolution"]) # 0.25
# Access time resolution
print(parsed["time"]["time_resolution"]["interval"]) # "1 day"
print(parsed["time"]["time_resolution"]["regular"]) # True
# Access all timesteps
print(parsed["time"]["timesteps"][:5]) # First 5 timesteps🔧 Improvements
- Added 12 comprehensive test cases for sinfo parsing
- Improved parser robustness and error handling
- Better documentation with detailed docstrings
Backward Compatibility
Fully backward compatible - all existing code continues to work without changes.
Installation
pip install --upgrade python-cdo-wrapperFull Changelog: V0.2.0...V0.2.1
V0.2.0 - Structured Output Support
🎉 New Features
- Structured Dictionary Output: Add
return_dict=Trueparameter tocdo()function - Smart Parsers: Automatic parsing of CDO text output into Python dictionaries
- Type Safety: TypedDict definitions for all structured outputs
- 8 Parser Implementations: Support for griddes, sinfo, showatts, zaxisdes, partab, vct, and more
📋 Supported Commands
- Grid Info:
griddes,griddes2,zaxisdes - Dataset Info:
sinfo,info,infon,infov,sinfon,sinfov - Variable Info:
vlist,showatts,showattsglob - Parameter Tables:
partab,codetab,vct,vct2
📖 Example Usage
from python_cdo_wrapper import cdo
# New: Get structured dictionary output
grid_info = cdo("griddes data.nc", return_dict=True)
print(grid_info['gridtype']) # 'lonlat'
print(grid_info['xsize']) # 360
# Old: Still works - returns string
grid_text = cdo("griddes data.nc")Backward Compatibility
Fully backward compatible - all existing code continues to work without changes.
🔧 Installation
pip install --upgrade python-cdo-wrapperFull Changelog: V0.1.0...V0.2.0
V0.1.0 - Initial Release
Fix ruff version mismatch