sync
Some checks failed
CI / Rust Tests (push) Waiting to run
CI / Python Tests (macos-latest, 3.11) (push) Waiting to run
CI / Python Tests (macos-latest, 3.13) (push) Waiting to run
CI / Python Tests (macos-latest, 3.8) (push) Waiting to run
CI / Python Tests (ubuntu-latest, 3.11) (push) Waiting to run
CI / Python Tests (ubuntu-latest, 3.13) (push) Waiting to run
CI / Python Tests (ubuntu-latest, 3.8) (push) Waiting to run
CI / Python Tests (windows-latest, 3.11) (push) Waiting to run
CI / Python Tests (windows-latest, 3.13) (push) Waiting to run
CI / Python Tests (windows-latest, 3.8) (push) Waiting to run
CI / Check Formatting (push) Waiting to run
Build Wheels (cibuildwheel) / Build wheels on Linux (aarch64) (push) Has been cancelled
Build Wheels (cibuildwheel) / Build wheels on Linux (x86_64) (push) Has been cancelled
Build Wheels (cibuildwheel) / Build wheels on macOS (arm64) (push) Has been cancelled
Build Wheels (cibuildwheel) / Build wheels on macOS (x86_64) (push) Has been cancelled
Build Wheels (cibuildwheel) / Build wheels on Windows (push) Has been cancelled
Build Wheels (cibuildwheel) / Build source distribution (push) Has been cancelled
Build Wheels (cibuildwheel) / Publish to GitHub Release (push) Has been cancelled
Some checks failed
CI / Rust Tests (push) Waiting to run
CI / Python Tests (macos-latest, 3.11) (push) Waiting to run
CI / Python Tests (macos-latest, 3.13) (push) Waiting to run
CI / Python Tests (macos-latest, 3.8) (push) Waiting to run
CI / Python Tests (ubuntu-latest, 3.11) (push) Waiting to run
CI / Python Tests (ubuntu-latest, 3.13) (push) Waiting to run
CI / Python Tests (ubuntu-latest, 3.8) (push) Waiting to run
CI / Python Tests (windows-latest, 3.11) (push) Waiting to run
CI / Python Tests (windows-latest, 3.13) (push) Waiting to run
CI / Python Tests (windows-latest, 3.8) (push) Waiting to run
CI / Check Formatting (push) Waiting to run
Build Wheels (cibuildwheel) / Build wheels on Linux (aarch64) (push) Has been cancelled
Build Wheels (cibuildwheel) / Build wheels on Linux (x86_64) (push) Has been cancelled
Build Wheels (cibuildwheel) / Build wheels on macOS (arm64) (push) Has been cancelled
Build Wheels (cibuildwheel) / Build wheels on macOS (x86_64) (push) Has been cancelled
Build Wheels (cibuildwheel) / Build wheels on Windows (push) Has been cancelled
Build Wheels (cibuildwheel) / Build source distribution (push) Has been cancelled
Build Wheels (cibuildwheel) / Publish to GitHub Release (push) Has been cancelled
This commit is contained in:
parent
2ae2d579b8
commit
d3f020b6c3
154
.github/workflows/build-wheels-cibuildwheel.yml
vendored
Normal file
154
.github/workflows/build-wheels-cibuildwheel.yml
vendored
Normal file
@ -0,0 +1,154 @@
|
||||
name: Build Wheels (cibuildwheel)
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
- master
|
||||
tags:
|
||||
- 'v*'
|
||||
pull_request:
|
||||
branches:
|
||||
- main
|
||||
- master
|
||||
workflow_dispatch:
|
||||
|
||||
env:
|
||||
CIBW_BUILD: cp38-* cp39-* cp310-* cp311-* cp312-* cp313-*
|
||||
CIBW_SKIP: "*-musllinux_* *-win32 *-manylinux_i686"
|
||||
CIBW_ARCHS_MACOS: "x86_64 arm64"
|
||||
CIBW_ARCHS_LINUX: "x86_64 aarch64"
|
||||
CIBW_ARCHS_WINDOWS: "AMD64"
|
||||
# Build with maturin
|
||||
CIBW_BEFORE_BUILD: pip install maturin
|
||||
CIBW_BUILD_FRONTEND: build
|
||||
|
||||
jobs:
|
||||
build-wheels-linux:
|
||||
name: Build wheels on Linux
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
target: [x86_64, aarch64]
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: recursive
|
||||
|
||||
- name: Set up QEMU
|
||||
if: matrix.target == 'aarch64'
|
||||
uses: docker/setup-qemu-action@v3
|
||||
with:
|
||||
platforms: arm64
|
||||
|
||||
- name: Build wheels
|
||||
uses: pypa/cibuildwheel@v2.16
|
||||
with:
|
||||
package-dir: rbufrp
|
||||
output-dir: wheelhouse
|
||||
env:
|
||||
CIBW_ARCHS_LINUX: ${{ matrix.target }}
|
||||
|
||||
- uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: wheels-linux-${{ matrix.target }}
|
||||
path: ./wheelhouse/*.whl
|
||||
if-no-files-found: error
|
||||
|
||||
build-wheels-macos:
|
||||
name: Build wheels on macOS
|
||||
runs-on: macos-latest
|
||||
strategy:
|
||||
matrix:
|
||||
target: [x86_64, arm64]
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: recursive
|
||||
|
||||
- name: Build wheels
|
||||
uses: pypa/cibuildwheel@v2.16
|
||||
with:
|
||||
package-dir: rbufrp
|
||||
output-dir: wheelhouse
|
||||
env:
|
||||
CIBW_ARCHS_MACOS: ${{ matrix.target }}
|
||||
|
||||
- uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: wheels-macos-${{ matrix.target }}
|
||||
path: ./wheelhouse/*.whl
|
||||
if-no-files-found: error
|
||||
|
||||
build-wheels-windows:
|
||||
name: Build wheels on Windows
|
||||
runs-on: windows-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: recursive
|
||||
|
||||
- name: Build wheels
|
||||
uses: pypa/cibuildwheel@v2.16
|
||||
with:
|
||||
package-dir: rbufrp
|
||||
output-dir: wheelhouse
|
||||
|
||||
- uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: wheels-windows-amd64
|
||||
path: ./wheelhouse/*.whl
|
||||
if-no-files-found: error
|
||||
|
||||
build-sdist:
|
||||
name: Build source distribution
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: recursive
|
||||
|
||||
- name: Set up Python
|
||||
uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: '3.11'
|
||||
|
||||
- name: Install dependencies
|
||||
run: pip install maturin build
|
||||
|
||||
- name: Build sdist
|
||||
working-directory: rbufrp
|
||||
run: maturin sdist --out dist
|
||||
|
||||
- name: Upload sdist
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: sdist
|
||||
path: rbufrp/dist/*.tar.gz
|
||||
if-no-files-found: error
|
||||
|
||||
publish-to-github-release:
|
||||
name: Publish to GitHub Release
|
||||
needs: [build-wheels-linux, build-wheels-macos, build-wheels-windows, build-sdist]
|
||||
runs-on: ubuntu-latest
|
||||
if: startsWith(github.ref, 'refs/tags/v')
|
||||
permissions:
|
||||
contents: write
|
||||
|
||||
steps:
|
||||
- name: Download all artifacts
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
path: dist
|
||||
merge-multiple: true
|
||||
|
||||
- name: Create GitHub Release
|
||||
uses: softprops/action-gh-release@v1
|
||||
with:
|
||||
files: dist/*
|
||||
generate_release_notes: true
|
||||
draft: false
|
||||
prerelease: false
|
||||
103
.github/workflows/ci.yml
vendored
Normal file
103
.github/workflows/ci.yml
vendored
Normal file
@ -0,0 +1,103 @@
|
||||
name: CI
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
- master
|
||||
pull_request:
|
||||
|
||||
jobs:
|
||||
rust-test:
|
||||
name: Rust Tests
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: recursive
|
||||
|
||||
- name: Set up Rust
|
||||
uses: dtolnay/rust-toolchain@stable
|
||||
|
||||
- name: Cache cargo registry
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: ~/.cargo/registry
|
||||
key: ${{ runner.os }}-cargo-registry-${{ hashFiles('**/Cargo.lock') }}
|
||||
|
||||
- name: Cache cargo index
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: ~/.cargo/git
|
||||
key: ${{ runner.os }}-cargo-git-${{ hashFiles('**/Cargo.lock') }}
|
||||
|
||||
- name: Cache cargo build
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: target
|
||||
key: ${{ runner.os }}-cargo-build-target-${{ hashFiles('**/Cargo.lock') }}
|
||||
|
||||
- name: Run cargo check
|
||||
run: cargo check --all --verbose
|
||||
|
||||
- name: Run cargo test
|
||||
run: cargo test --all --verbose
|
||||
|
||||
- name: Run cargo clippy
|
||||
run: cargo clippy --all -- -D warnings
|
||||
|
||||
- name: Run cargo fmt check
|
||||
run: cargo fmt --all -- --check
|
||||
|
||||
python-test:
|
||||
name: Python Tests
|
||||
runs-on: ${{ matrix.os }}
|
||||
strategy:
|
||||
matrix:
|
||||
os: [ubuntu-latest, macos-latest, windows-latest]
|
||||
python-version: ['3.8', '3.11', '3.13']
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: recursive
|
||||
|
||||
- name: Set up Python ${{ matrix.python-version }}
|
||||
uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: ${{ matrix.python-version }}
|
||||
|
||||
- name: Set up Rust
|
||||
uses: dtolnay/rust-toolchain@stable
|
||||
|
||||
- name: Install maturin
|
||||
run: pip install maturin pytest
|
||||
|
||||
- name: Build Python package
|
||||
working-directory: rbufrp
|
||||
run: maturin develop --release
|
||||
|
||||
- name: Test Python package
|
||||
working-directory: rbufrp
|
||||
run: |
|
||||
python -c "import rbufrp; print(rbufrp.__version__)"
|
||||
python -c "import rbufrp; print(rbufrp.get_tables_path())"
|
||||
|
||||
check-formatting:
|
||||
name: Check Formatting
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Set up Python
|
||||
uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: '3.11'
|
||||
|
||||
- name: Install ruff
|
||||
run: pip install ruff
|
||||
|
||||
- name: Check Python formatting with ruff
|
||||
run: |
|
||||
ruff check rbufrp/src/rbufrp/
|
||||
ruff format --check rbufrp/src/rbufrp/
|
||||
1
BUFR4
Submodule
1
BUFR4
Submodule
@ -0,0 +1 @@
|
||||
Subproject commit a6b7ab078d4c70c69565655f7cf7c7d3913d6d78
|
||||
@ -21,6 +21,7 @@ rustc-hash = "2.1.1"
|
||||
[features]
|
||||
default = ["opera"]
|
||||
opera = ["gentools/opera"]
|
||||
python_bindings = []
|
||||
|
||||
|
||||
[profile.bench]
|
||||
|
||||
@ -11,7 +11,11 @@ use genlib::{
|
||||
prelude::{BUFRTableB, BUFRTableBitMap, BUFRTableD},
|
||||
tables::{ArchivedBTableEntry, ArchivedDTableEntry},
|
||||
};
|
||||
use std::{fmt::Display, ops::Deref};
|
||||
use std::{
|
||||
borrow::{Borrow, Cow},
|
||||
fmt::Display,
|
||||
ops::Deref,
|
||||
};
|
||||
|
||||
const MISS_VAL: f64 = 99999.999999;
|
||||
|
||||
@ -1376,6 +1380,7 @@ impl<'a, 'b> Container<'a> for Repeating<'a, 'b> {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct BUFRParsed<'a> {
|
||||
records: Vec<BUFRRecord<'a>>,
|
||||
}
|
||||
@ -1385,11 +1390,11 @@ impl<'a> BUFRParsed<'a> {
|
||||
Self { records: vec![] }
|
||||
}
|
||||
|
||||
pub fn push(&mut self, value: Value, element_name: &'a str, unit: &'a str) {
|
||||
fn push(&mut self, value: Value, element_name: &'a str, unit: &'a str) {
|
||||
self.records.push(BUFRRecord {
|
||||
name: Some(element_name),
|
||||
name: Some(Cow::Borrowed(element_name)),
|
||||
values: BUFRData::Single(value),
|
||||
unit: Some(unit),
|
||||
unit: Some(Cow::Borrowed(unit)),
|
||||
});
|
||||
}
|
||||
|
||||
@ -1406,6 +1411,12 @@ impl<'a> BUFRParsed<'a> {
|
||||
values: Vec::with_capacity(time),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn into_owned(&self) -> BUFRParsed<'static> {
|
||||
BUFRParsed {
|
||||
records: self.records.iter().map(|r| r.into_owned()).collect(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct Array<'a, 's> {
|
||||
@ -1424,9 +1435,9 @@ impl<'a> Array<'a, '_> {
|
||||
|
||||
fn finish(self, name: Option<&'a str>, unit: Option<&'a str>) {
|
||||
let recording = BUFRRecord {
|
||||
name,
|
||||
name: name.map(|n| Cow::Borrowed(n)),
|
||||
values: BUFRData::Array(self.values),
|
||||
unit,
|
||||
unit: unit.map(|u| Cow::Borrowed(u)),
|
||||
};
|
||||
self.parsed.records.push(recording);
|
||||
}
|
||||
@ -1452,21 +1463,38 @@ impl<'a, 's> Repeating<'a, 's> {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum BUFRData {
|
||||
Repeat(Vec<Value>),
|
||||
Single(Value),
|
||||
Array(Vec<f64>),
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct BUFRRecord<'a> {
|
||||
pub name: Option<&'a str>,
|
||||
// pub name: Option<&'a str>,
|
||||
pub name: Option<Cow<'a, str>>,
|
||||
pub values: BUFRData,
|
||||
pub unit: Option<&'a str>,
|
||||
pub unit: Option<Cow<'a, str>>,
|
||||
}
|
||||
|
||||
impl BUFRRecord<'_> {
|
||||
pub fn into_owned(&self) -> BUFRRecord<'static> {
|
||||
BUFRRecord {
|
||||
name: self.name.as_ref().map(|s| Cow::Owned(s.to_string())),
|
||||
values: match &self.values {
|
||||
BUFRData::Single(v) => BUFRData::Single(v.clone()),
|
||||
BUFRData::Repeat(vs) => BUFRData::Repeat(vs.clone()),
|
||||
BUFRData::Array(a) => BUFRData::Array(a.clone()),
|
||||
},
|
||||
unit: self.unit.as_ref().map(|s| Cow::Owned(s.to_string())),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for BUFRRecord<'_> {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
let is_print_unit = match self.unit {
|
||||
let is_print_unit = match self.unit.as_ref().map(|s| &**s) {
|
||||
Some("CAITT IA5" | "code table" | "code-table" | "flag table" | "flag-table") => false,
|
||||
None => false,
|
||||
_ => true,
|
||||
@ -1477,79 +1505,33 @@ impl Display for BUFRRecord<'_> {
|
||||
}
|
||||
|
||||
let name = self.name.as_ref().unwrap();
|
||||
let width = f.width().unwrap_or(0);
|
||||
|
||||
match &self.values {
|
||||
BUFRData::Single(v) => {
|
||||
if is_print_unit {
|
||||
write!(f, "{}: {} {}", name, v, self.unit.as_ref().unwrap())?;
|
||||
if width > 0 {
|
||||
write!(f, "{:<width$} : ", name, width = width)?;
|
||||
} else {
|
||||
write!(f, "{}: {}", name, v)?;
|
||||
write!(f, "{} : ", name)?;
|
||||
}
|
||||
|
||||
match v {
|
||||
Value::Missing => write!(f, "MISSING")?,
|
||||
Value::String(s) => write!(f, "\"{}\"", s)?,
|
||||
Value::Number(n) => {
|
||||
if is_print_unit {
|
||||
write!(f, "{:>12.6} {}", n, self.unit.as_ref().unwrap())?;
|
||||
} else {
|
||||
write!(f, "{}", n)?;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
BUFRData::Repeat(vs) => {
|
||||
if vs.len() < 8 {
|
||||
write!(f, "{}: [", name)?;
|
||||
for v in vs {
|
||||
if is_print_unit {
|
||||
write!(f, "{} {}, ", v, self.unit.as_ref().unwrap())?;
|
||||
} else {
|
||||
write!(f, "{}, ", v)?;
|
||||
self.format_sequence(f, name, vs, is_print_unit, width)?;
|
||||
}
|
||||
}
|
||||
|
||||
write!(f, "]")?;
|
||||
} else {
|
||||
write!(f, "{}: [", name)?;
|
||||
for v in &vs[0..5] {
|
||||
if is_print_unit {
|
||||
write!(f, "{} {}, ", v, self.unit.as_ref().unwrap())?;
|
||||
} else {
|
||||
write!(f, "{}, ", v)?;
|
||||
}
|
||||
}
|
||||
write!(f, "... ")?;
|
||||
for v in &vs[vs.len() - 2..vs.len()] {
|
||||
if is_print_unit {
|
||||
write!(f, "{} {}, ", v, self.unit.as_ref().unwrap())?;
|
||||
} else {
|
||||
write!(f, "{}, ", v)?;
|
||||
}
|
||||
}
|
||||
write!(f, "]")?;
|
||||
}
|
||||
}
|
||||
|
||||
BUFRData::Array(a) => {
|
||||
if a.len() < 8 {
|
||||
write!(f, "{}: [", name)?;
|
||||
for v in a {
|
||||
if is_print_unit {
|
||||
write!(f, "{} {}, ", v, self.unit.as_ref().unwrap())?;
|
||||
} else {
|
||||
write!(f, "{}, ", v)?;
|
||||
}
|
||||
}
|
||||
|
||||
write!(f, "]")?;
|
||||
} else {
|
||||
write!(f, "{}: [", name)?;
|
||||
for v in &a[0..5] {
|
||||
if is_print_unit {
|
||||
write!(f, "{} {}, ", v, self.unit.as_ref().unwrap())?;
|
||||
} else {
|
||||
write!(f, "{}, ", v)?;
|
||||
}
|
||||
}
|
||||
write!(f, "... ")?;
|
||||
for v in &a[a.len() - 2..a.len()] {
|
||||
if is_print_unit {
|
||||
write!(f, "{} {}, ", v, self.unit.as_ref().unwrap())?;
|
||||
} else {
|
||||
write!(f, "{}, ", v)?;
|
||||
}
|
||||
}
|
||||
write!(f, "]")?;
|
||||
}
|
||||
self.format_array(f, name, a, is_print_unit, width)?;
|
||||
}
|
||||
}
|
||||
|
||||
@ -1557,15 +1539,283 @@ impl Display for BUFRRecord<'_> {
|
||||
}
|
||||
}
|
||||
|
||||
impl BUFRRecord<'_> {
|
||||
fn format_sequence(
|
||||
&self,
|
||||
f: &mut std::fmt::Formatter<'_>,
|
||||
name: &str,
|
||||
values: &[Value],
|
||||
is_print_unit: bool,
|
||||
width: usize,
|
||||
) -> std::fmt::Result {
|
||||
let missing_count = values.iter().filter(|v| v.is_missing()).count();
|
||||
|
||||
if width > 0 {
|
||||
write!(f, "{:<width$} : ", name, width = width)?;
|
||||
} else {
|
||||
write!(f, "{} : ", name)?;
|
||||
}
|
||||
|
||||
write!(f, "[len={}", values.len())?;
|
||||
if missing_count > 0 {
|
||||
write!(f, ", missing={}", missing_count)?;
|
||||
}
|
||||
write!(f, "] ")?;
|
||||
|
||||
if values.is_empty() {
|
||||
write!(f, "[]")?;
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let show_limit = 6;
|
||||
if values.len() <= show_limit {
|
||||
write!(f, "[")?;
|
||||
for (i, v) in values.iter().enumerate() {
|
||||
if i > 0 {
|
||||
write!(f, ", ")?;
|
||||
}
|
||||
self.format_value(f, v, is_print_unit)?;
|
||||
}
|
||||
write!(f, "]")?;
|
||||
} else {
|
||||
write!(f, "[")?;
|
||||
for (i, v) in values.iter().take(3).enumerate() {
|
||||
if i > 0 {
|
||||
write!(f, ", ")?;
|
||||
}
|
||||
self.format_value(f, v, is_print_unit)?;
|
||||
}
|
||||
write!(f, " ... ")?;
|
||||
for (i, v) in values.iter().skip(values.len() - 2).enumerate() {
|
||||
if i > 0 {
|
||||
write!(f, ", ")?;
|
||||
}
|
||||
self.format_value(f, v, is_print_unit)?;
|
||||
}
|
||||
write!(f, "]")?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn format_array(
|
||||
&self,
|
||||
f: &mut std::fmt::Formatter<'_>,
|
||||
name: &str,
|
||||
values: &[f64],
|
||||
is_print_unit: bool,
|
||||
width: usize,
|
||||
) -> std::fmt::Result {
|
||||
let missing_count = values.iter().filter(|&&v| v == MISS_VAL).count();
|
||||
let valid_values: Vec<f64> = values.iter().copied().filter(|&v| v != MISS_VAL).collect();
|
||||
|
||||
if width > 0 {
|
||||
write!(f, "{:<width$} : ", name, width = width)?;
|
||||
} else {
|
||||
write!(f, "{} : ", name)?;
|
||||
}
|
||||
|
||||
write!(f, "[len={}", values.len())?;
|
||||
if missing_count > 0 {
|
||||
write!(f, ", missing={}", missing_count)?;
|
||||
}
|
||||
|
||||
// 显示统计信息
|
||||
if !valid_values.is_empty() {
|
||||
let min = valid_values.iter().copied().fold(f64::INFINITY, f64::min);
|
||||
let max = valid_values
|
||||
.iter()
|
||||
.copied()
|
||||
.fold(f64::NEG_INFINITY, f64::max);
|
||||
let mean = valid_values.iter().sum::<f64>() / valid_values.len() as f64;
|
||||
|
||||
write!(f, ", min={:.3}, max={:.3}, mean={:.3}", min, max, mean)?;
|
||||
}
|
||||
write!(f, "]")?;
|
||||
|
||||
if is_print_unit {
|
||||
if let Some(unit) = &self.unit {
|
||||
write!(f, " {}", unit)?;
|
||||
}
|
||||
}
|
||||
|
||||
// 显示样例值
|
||||
if !values.is_empty() {
|
||||
let show_limit = 6;
|
||||
if values.len() <= show_limit {
|
||||
write!(f, "\n [")?;
|
||||
for (i, v) in values.iter().enumerate() {
|
||||
if i > 0 {
|
||||
write!(f, ", ")?;
|
||||
}
|
||||
if *v == MISS_VAL {
|
||||
write!(f, "MISSING")?;
|
||||
} else {
|
||||
write!(f, "{:.3}", v)?;
|
||||
}
|
||||
}
|
||||
write!(f, "]")?;
|
||||
} else {
|
||||
write!(f, "\n [")?;
|
||||
for (i, v) in values.iter().take(3).enumerate() {
|
||||
if i > 0 {
|
||||
write!(f, ", ")?;
|
||||
}
|
||||
if *v == MISS_VAL {
|
||||
write!(f, "MISSING")?;
|
||||
} else {
|
||||
write!(f, "{:.3}", v)?;
|
||||
}
|
||||
}
|
||||
write!(f, " ... ")?;
|
||||
for (i, v) in values.iter().skip(values.len() - 2).enumerate() {
|
||||
if i > 0 {
|
||||
write!(f, ", ")?;
|
||||
}
|
||||
if *v == MISS_VAL {
|
||||
write!(f, "MISSING")?;
|
||||
} else {
|
||||
write!(f, "{:.3}", v)?;
|
||||
}
|
||||
}
|
||||
write!(f, "]")?;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn format_value(
|
||||
&self,
|
||||
f: &mut std::fmt::Formatter<'_>,
|
||||
value: &Value,
|
||||
is_print_unit: bool,
|
||||
) -> std::fmt::Result {
|
||||
match value {
|
||||
Value::Missing => write!(f, "MISSING"),
|
||||
Value::String(s) => write!(f, "\"{}\"", s),
|
||||
Value::Number(n) => {
|
||||
if is_print_unit {
|
||||
write!(f, "{:.3}", n)
|
||||
} else {
|
||||
write!(f, "{}", n)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for BUFRParsed<'_> {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
writeln!(f, "BUFR Parsed Data ({} records)", self.records.len())?;
|
||||
|
||||
// 计算最长的名称长度用于对齐
|
||||
let max_name_len = self
|
||||
.records
|
||||
.iter()
|
||||
.filter_map(|r| r.name.as_ref())
|
||||
.map(|n| n.len())
|
||||
.max()
|
||||
.unwrap_or(0)
|
||||
.min(50); // 限制最大宽度
|
||||
|
||||
for record in &self.records {
|
||||
writeln!(f, "{:<max_name_len$}", record, max_name_len = max_name_len)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl BUFRParsed<'_> {
|
||||
/// 获取记录数量
|
||||
pub fn record_count(&self) -> usize {
|
||||
self.records.len()
|
||||
}
|
||||
|
||||
/// 获取所有记录
|
||||
pub fn records(&self) -> &[BUFRRecord<'_>] {
|
||||
&self.records
|
||||
}
|
||||
|
||||
/// 紧凑格式显示(不带边框和统计信息)
|
||||
pub fn display_compact(&self) -> CompactDisplay<'_> {
|
||||
CompactDisplay(self)
|
||||
}
|
||||
|
||||
/// 详细格式显示(包含更多元数据)
|
||||
pub fn display_detailed(&self) -> DetailedDisplay<'_> {
|
||||
DetailedDisplay(self)
|
||||
}
|
||||
}
|
||||
|
||||
/// 紧凑显示包装器
|
||||
pub struct CompactDisplay<'a>(&'a BUFRParsed<'a>);
|
||||
|
||||
impl Display for CompactDisplay<'_> {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
for record in &self.0.records {
|
||||
writeln!(f, "{}", record)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
/// 详细显示包装器
|
||||
pub struct DetailedDisplay<'a>(&'a BUFRParsed<'a>);
|
||||
|
||||
impl Display for DetailedDisplay<'_> {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
writeln!(f, "BUFR Parsed Data - Detailed View")?;
|
||||
writeln!(f)?;
|
||||
|
||||
// 统计信息
|
||||
let total_records = self.0.records.len();
|
||||
let single_count = self
|
||||
.0
|
||||
.records
|
||||
.iter()
|
||||
.filter(|r| matches!(r.values, BUFRData::Single(_)))
|
||||
.count();
|
||||
let array_count = self
|
||||
.0
|
||||
.records
|
||||
.iter()
|
||||
.filter(|r| matches!(r.values, BUFRData::Array(_)))
|
||||
.count();
|
||||
let repeat_count = self
|
||||
.0
|
||||
.records
|
||||
.iter()
|
||||
.filter(|r| matches!(r.values, BUFRData::Repeat(_)))
|
||||
.count();
|
||||
|
||||
writeln!(f, "Statistics:")?;
|
||||
writeln!(f, " Total records: {}", total_records)?;
|
||||
writeln!(f, " Single values: {}", single_count)?;
|
||||
writeln!(f, " Arrays: {}", array_count)?;
|
||||
writeln!(f, " Repeated values: {}", repeat_count)?;
|
||||
writeln!(f)?;
|
||||
|
||||
// 详细记录
|
||||
let max_name_len = self
|
||||
.0
|
||||
.records
|
||||
.iter()
|
||||
.filter_map(|r| r.name.as_ref())
|
||||
.map(|n| n.len())
|
||||
.max()
|
||||
.unwrap_or(0)
|
||||
.min(50);
|
||||
|
||||
for (idx, record) in self.0.records.iter().enumerate() {
|
||||
writeln!(f, "Record {}: {:<max_name_len$}", idx + 1, record, max_name_len = max_name_len)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
enum Frame<'v, 'a> {
|
||||
Slice {
|
||||
descs: Descs<'v>,
|
||||
|
||||
@ -5,7 +5,9 @@ pub mod errors;
|
||||
pub mod opera;
|
||||
pub mod parser;
|
||||
pub mod structs;
|
||||
pub mod table_path;
|
||||
pub mod tables;
|
||||
|
||||
pub use crate::decoder::Decoder;
|
||||
pub use crate::parser::*;
|
||||
pub use crate::table_path::{get_tables_base_path, set_tables_base_path};
|
||||
|
||||
65
rbufr/src/table_path.rs
Normal file
65
rbufr/src/table_path.rs
Normal file
@ -0,0 +1,65 @@
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::sync::OnceLock;
|
||||
|
||||
static TABLES_BASE_PATH: OnceLock<PathBuf> = OnceLock::new();
|
||||
|
||||
pub fn set_tables_base_path<P: AsRef<Path>>(path: P) {
|
||||
let _ = TABLES_BASE_PATH.set(path.as_ref().to_path_buf());
|
||||
}
|
||||
|
||||
pub fn get_tables_base_path() -> PathBuf {
|
||||
if let Some(path) = TABLES_BASE_PATH.get() {
|
||||
return path.clone();
|
||||
}
|
||||
|
||||
if let Ok(env_path) = std::env::var("RBUFR_TABLES_PATH") {
|
||||
return PathBuf::from(env_path);
|
||||
}
|
||||
|
||||
#[cfg(feature = "python_bindings")]
|
||||
if let Some(python_path) = try_find_python_package_path() {
|
||||
return python_path;
|
||||
}
|
||||
|
||||
PathBuf::from("tables")
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
fn try_find_python_package_path() -> Option<PathBuf> {
|
||||
if let Ok(exe_path) = std::env::current_exe() {
|
||||
let mut candidate = exe_path.parent()?.to_path_buf();
|
||||
candidate.push("rbufrp");
|
||||
candidate.push("tables");
|
||||
if candidate.exists() {
|
||||
return Some(candidate);
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
pub fn get_table_path<P: AsRef<Path>>(relative_path: P) -> PathBuf {
|
||||
let base = get_tables_base_path();
|
||||
base.join(relative_path)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_set_and_get_path() {
|
||||
set_tables_base_path("/custom/tables/path");
|
||||
let path = get_tables_base_path();
|
||||
assert_eq!(path, PathBuf::from("/custom/tables/path"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_get_table_path() {
|
||||
set_tables_base_path("/base");
|
||||
let table_path = get_table_path("master/BUFR_TableB_0.bufrtbl");
|
||||
assert_eq!(
|
||||
table_path,
|
||||
PathBuf::from("/base/master/BUFR_TableB_0.bufrtbl")
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -52,18 +52,16 @@ impl LocalTable {
|
||||
}
|
||||
impl TableTrait for MasterTable {
|
||||
fn file_path(&self, table_type: TableType) -> PathBuf {
|
||||
use crate::table_path::get_table_path;
|
||||
|
||||
match table_type {
|
||||
TableType::B => {
|
||||
let mut base_dir = PathBuf::new();
|
||||
base_dir.push("tables/master");
|
||||
let file_name = format!("BUFR_TableB_{}.bufrtbl", self.version);
|
||||
base_dir.join(file_name)
|
||||
let file_name = format!("master/BUFR_TableB_{}.bufrtbl", self.version);
|
||||
get_table_path(file_name)
|
||||
}
|
||||
TableType::D => {
|
||||
let mut base_dir = PathBuf::new();
|
||||
base_dir.push("tables/master");
|
||||
let file_name = format!("BUFR_TableD_{}.bufrtbl", self.version);
|
||||
base_dir.join(file_name)
|
||||
let file_name = format!("master/BUFR_TableD_{}.bufrtbl", self.version);
|
||||
get_table_path(file_name)
|
||||
}
|
||||
_ => {
|
||||
unreachable!("Table type not supported for MasterTable")
|
||||
@ -74,26 +72,24 @@ impl TableTrait for MasterTable {
|
||||
|
||||
impl TableTrait for LocalTable {
|
||||
fn file_path(&self, table_type: TableType) -> PathBuf {
|
||||
use crate::table_path::get_table_path;
|
||||
|
||||
match table_type {
|
||||
TableType::B => {
|
||||
let mut base_dir = PathBuf::new();
|
||||
base_dir.push("tables/local");
|
||||
let sub_center_str = match self.sub_center {
|
||||
Some(sc) => format!("{}", sc),
|
||||
None => "0".to_string(),
|
||||
};
|
||||
let file_name = format!("BUFR_TableB_{}_{}.bufrtbl", sub_center_str, self.version);
|
||||
base_dir.join(file_name)
|
||||
let file_name = format!("local/BUFR_TableB_{}_{}.bufrtbl", sub_center_str, self.version);
|
||||
get_table_path(file_name)
|
||||
}
|
||||
TableType::D => {
|
||||
let mut base_dir = PathBuf::new();
|
||||
base_dir.push("tables/local");
|
||||
let sub_center_str = match self.sub_center {
|
||||
Some(sc) => format!("{}", sc),
|
||||
None => "0".to_string(),
|
||||
};
|
||||
let file_name = format!("BUFR_TableD_{}_{}.bufrtbl", sub_center_str, self.version);
|
||||
base_dir.join(file_name)
|
||||
let file_name = format!("local/BUFR_TableD_{}_{}.bufrtbl", sub_center_str, self.version);
|
||||
get_table_path(file_name)
|
||||
}
|
||||
_ => {
|
||||
unreachable!("Table type not supported for LocalTable")
|
||||
@ -104,12 +100,12 @@ impl TableTrait for LocalTable {
|
||||
|
||||
impl TableTrait for BitmapTable {
|
||||
fn file_path(&self, table_type: TableType) -> PathBuf {
|
||||
use crate::table_path::get_table_path;
|
||||
|
||||
match table_type {
|
||||
TableType::BitMap => {
|
||||
let mut base_dir = PathBuf::new();
|
||||
base_dir.push("tables/opera");
|
||||
let file_name = format!("BUFR_Opera_Bitmap_{}.bufrtbl", self.center);
|
||||
base_dir.join(file_name)
|
||||
let file_name = format!("opera/BUFR_Opera_Bitmap_{}.bufrtbl", self.center);
|
||||
get_table_path(file_name)
|
||||
}
|
||||
_ => {
|
||||
unreachable!("Table type not supported for BitmapTable")
|
||||
|
||||
@ -13,4 +13,4 @@ crate-type = ["cdylib"]
|
||||
# "abi3-py39" tells pyo3 (and maturin) to build using the stable ABI with minimum Python version 3.9
|
||||
pyo3 = { version = "0.27.1", features = ["extension-module", "abi3-py38"] }
|
||||
|
||||
rbufr = { path = "../rbufr" }
|
||||
rbufr = { path = "../rbufr", features = ["python_bindings"] }
|
||||
|
||||
1
rbufrp/MANIFEST.in
Normal file
1
rbufrp/MANIFEST.in
Normal file
@ -0,0 +1 @@
|
||||
recursive-include ../rbufr/tables *.bufrtbl
|
||||
@ -3,9 +3,7 @@ name = "rbufrp"
|
||||
version = "0.1.0"
|
||||
description = "Add your description here"
|
||||
readme = "README.md"
|
||||
authors = [
|
||||
{ name = "Tsuki", email = "qwin7989@gmail.com" }
|
||||
]
|
||||
authors = [{ name = "Tsuki", email = "qwin7989@gmail.com" }]
|
||||
requires-python = ">=3.11"
|
||||
dependencies = []
|
||||
|
||||
@ -16,9 +14,18 @@ rbufrp = "rbufrp:main"
|
||||
module-name = "rbufrp._core"
|
||||
python-packages = ["rbufrp"]
|
||||
python-source = "src"
|
||||
include = [
|
||||
{ path = "../rbufr/tables", format = "sdist" },
|
||||
{ path = "../rbufr/tables", format = "wheel" },
|
||||
]
|
||||
|
||||
[tool.uv]
|
||||
cache-keys = [{ file = "pyproject.toml" }, { file = "src/**/*.rs" }, { file = "Cargo.toml" }, { file = "Cargo.lock" }]
|
||||
cache-keys = [
|
||||
{ file = "pyproject.toml" },
|
||||
{ file = "src/**/*.rs" },
|
||||
{ file = "Cargo.toml" },
|
||||
{ file = "Cargo.lock" },
|
||||
]
|
||||
|
||||
[build-system]
|
||||
requires = ["maturin>=1.0,<2.0"]
|
||||
|
||||
@ -1,19 +1,26 @@
|
||||
use librbufr::{Decoder, parse};
|
||||
use pyo3::prelude::*;
|
||||
|
||||
#[pymodule]
|
||||
mod _core {
|
||||
use librbufr::{
|
||||
Decoder,
|
||||
block::{BUFRFile as IB, MessageBlock as IM},
|
||||
decoder::BUFRData,
|
||||
decoder::BUFRParsed as _BUFRParsed,
|
||||
errors::Error,
|
||||
parse,
|
||||
get_tables_base_path, parse, set_tables_base_path,
|
||||
};
|
||||
use pyo3::prelude::*;
|
||||
|
||||
#[pyfunction]
|
||||
fn hello_from_bin() -> String {
|
||||
"Hello from rbufrp!".to_string()
|
||||
fn set_tables_path(path: &str) -> PyResult<()> {
|
||||
set_tables_base_path(path);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[pyfunction]
|
||||
fn get_tables_path() -> PyResult<String> {
|
||||
let path = get_tables_base_path();
|
||||
Ok(path.to_string_lossy().to_string())
|
||||
}
|
||||
|
||||
#[pyclass]
|
||||
@ -26,7 +33,7 @@ mod _core {
|
||||
BUFRDecoder {}
|
||||
}
|
||||
|
||||
fn decode_bufr(&self, file_path: &str) -> PyResult<BUFRFile> {
|
||||
fn decode(&self, file_path: &str) -> PyResult<BUFRFile> {
|
||||
let parsed = parse(file_path).map_err(|e| match e {
|
||||
Error::Io(io_err) => {
|
||||
PyErr::new::<pyo3::exceptions::PyIOError, _>(format!("IO Error: {}", io_err))
|
||||
@ -48,6 +55,24 @@ mod _core {
|
||||
|
||||
Ok(BUFRFile(parsed))
|
||||
}
|
||||
|
||||
fn parse_message(&self, message: &BUFRMessage) -> PyResult<BUFRParsed> {
|
||||
self._parse_message(message).map_err(|e| {
|
||||
PyErr::new::<pyo3::exceptions::PyException, _>(format!(
|
||||
"Error parsing BUFR message: {}",
|
||||
e
|
||||
))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl BUFRDecoder {
|
||||
fn _parse_message(&self, message: &BUFRMessage) -> librbufr::errors::Result<BUFRParsed> {
|
||||
let _message = &message.message;
|
||||
let mut decoder = Decoder::from_message(_message)?;
|
||||
let record = decoder.decode(_message)?.into_owned();
|
||||
Ok(BUFRParsed(record))
|
||||
}
|
||||
}
|
||||
|
||||
#[pyclass]
|
||||
@ -78,4 +103,25 @@ mod _core {
|
||||
struct BUFRMessage {
|
||||
message: IM,
|
||||
}
|
||||
|
||||
#[pymethods]
|
||||
impl BUFRMessage {
|
||||
fn __repr__(&self) -> String {
|
||||
format!("{}", self.message)
|
||||
}
|
||||
|
||||
fn version(&self) -> u8 {
|
||||
self.message.version()
|
||||
}
|
||||
}
|
||||
|
||||
#[pyclass]
|
||||
struct BUFRParsed(_BUFRParsed<'static>);
|
||||
|
||||
#[pymethods]
|
||||
impl BUFRParsed {
|
||||
fn __repr__(&self) -> String {
|
||||
format!("{}", &self.0)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,5 +1,93 @@
|
||||
from rbufrp._core import hello_from_bin
|
||||
"""
|
||||
rbufrp - BUFR (Binary Universal Form for the Representation of meteorological data) decoder
|
||||
"""
|
||||
|
||||
import os
|
||||
import sys
|
||||
from pathlib import Path
|
||||
from typing import Optional
|
||||
|
||||
# Import the Rust extension module
|
||||
from ._core import (
|
||||
set_tables_path,
|
||||
get_tables_path,
|
||||
BUFRDecoder,
|
||||
BUFRFile,
|
||||
BUFRMessage,
|
||||
BUFRParsed,
|
||||
)
|
||||
|
||||
__version__ = "0.1.0"
|
||||
__all__ = [
|
||||
"BUFRDecoder",
|
||||
"BUFRFile",
|
||||
"BUFRMessage",
|
||||
"BUFRParsed",
|
||||
"set_tables_path",
|
||||
"get_tables_path",
|
||||
"initialize_tables_path",
|
||||
]
|
||||
|
||||
|
||||
def _find_tables_directory() -> Optional[Path]:
|
||||
env_path = os.environ.get("RBUFR_TABLES_PATH")
|
||||
if env_path:
|
||||
tables_path = Path(env_path)
|
||||
if tables_path.exists() and tables_path.is_dir():
|
||||
return tables_path
|
||||
|
||||
package_dir = Path(__file__).parent
|
||||
installed_tables = package_dir / "tables"
|
||||
if installed_tables.exists() and installed_tables.is_dir():
|
||||
return installed_tables
|
||||
|
||||
dev_tables = package_dir.parent.parent.parent / "rbufr" / "tables"
|
||||
if dev_tables.exists() and dev_tables.is_dir():
|
||||
return dev_tables
|
||||
|
||||
cwd_tables = Path.cwd() / "tables"
|
||||
if cwd_tables.exists() and cwd_tables.is_dir():
|
||||
return cwd_tables
|
||||
|
||||
return None
|
||||
|
||||
|
||||
def initialize_tables_path(custom_path: Optional[str | Path] = None) -> None:
|
||||
if custom_path:
|
||||
custom_path = Path(custom_path)
|
||||
if not custom_path.exists():
|
||||
raise RuntimeError(f"指定的 tables 路径不存在: {custom_path}")
|
||||
set_tables_path(str(custom_path.absolute()))
|
||||
return
|
||||
|
||||
tables_dir = _find_tables_directory()
|
||||
if tables_dir is None:
|
||||
raise RuntimeError(
|
||||
"无法找到 BUFR tables 目录。请执行以下操作之一:\n"
|
||||
"1. 设置环境变量 RBUFR_TABLES_PATH\n"
|
||||
"2. 使用 initialize_tables_path('/path/to/tables') 手动指定\n"
|
||||
"3. 确保在包含 tables 目录的位置运行"
|
||||
)
|
||||
|
||||
set_tables_path(str(tables_dir.absolute()))
|
||||
|
||||
|
||||
# 自动初始化 tables 路径
|
||||
try:
|
||||
initialize_tables_path()
|
||||
except RuntimeError as e:
|
||||
import warnings
|
||||
warnings.warn(
|
||||
f"Tables 路径自动初始化失败: {e}\n"
|
||||
"您可以稍后手动调用 rbufrp.initialize_tables_path() 来设置",
|
||||
UserWarning
|
||||
)
|
||||
|
||||
|
||||
def main() -> None:
|
||||
print(hello_from_bin())
|
||||
"""命令行入口点"""
|
||||
print(f"Tables path: {get_tables_path()}")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
|
||||
BIN
rbufrp/src/rbufrp/__pycache__/__init__.cpython-312.pyc
Normal file
BIN
rbufrp/src/rbufrp/__pycache__/__init__.cpython-312.pyc
Normal file
Binary file not shown.
Binary file not shown.
@ -1 +1,151 @@
|
||||
def hello_from_bin() -> str: ...
|
||||
"""
|
||||
Type stubs for rbufrp._core
|
||||
|
||||
This file provides type hints for the Rust extension module.
|
||||
"""
|
||||
|
||||
from typing import Optional
|
||||
|
||||
class BUFRDecoder:
|
||||
"""BUFR decoder for parsing BUFR files."""
|
||||
|
||||
def __init__(self) -> None:
|
||||
"""Create a new BUFR decoder instance."""
|
||||
...
|
||||
|
||||
def decode(self, file_path: str) -> BUFRFile:
|
||||
"""
|
||||
Decode a BUFR file from the given path.
|
||||
|
||||
Args:
|
||||
file_path: Path to the BUFR file to decode
|
||||
|
||||
Returns:
|
||||
BUFRFile: Parsed BUFR file containing messages
|
||||
|
||||
Raises:
|
||||
IOError: If the file cannot be read
|
||||
ValueError: If the file is not a valid BUFR file
|
||||
"""
|
||||
...
|
||||
|
||||
def parse_message(self, message: BUFRMessage) -> BUFRParsed:
|
||||
"""
|
||||
Parse a single BUFR message.
|
||||
|
||||
Args:
|
||||
message: The BUFR message to parse
|
||||
|
||||
Returns:
|
||||
BUFRParsed: Parsed data from the message
|
||||
|
||||
Raises:
|
||||
Exception: If parsing fails
|
||||
"""
|
||||
...
|
||||
|
||||
class BUFRFile:
|
||||
"""
|
||||
Represents a parsed BUFR file containing one or more messages.
|
||||
"""
|
||||
|
||||
def __repr__(self) -> str:
|
||||
"""Return a string representation of the BUFR file."""
|
||||
...
|
||||
|
||||
def message_count(self) -> int:
|
||||
"""
|
||||
Get the number of messages in the file.
|
||||
|
||||
Returns:
|
||||
int: Number of BUFR messages
|
||||
"""
|
||||
...
|
||||
|
||||
def get_message(self, index: int) -> BUFRMessage:
|
||||
"""
|
||||
Get a specific message by index.
|
||||
|
||||
Args:
|
||||
index: Zero-based index of the message
|
||||
|
||||
Returns:
|
||||
BUFRMessage: The requested message
|
||||
|
||||
Raises:
|
||||
IndexError: If the index is out of range
|
||||
"""
|
||||
...
|
||||
|
||||
class BUFRMessage:
|
||||
"""
|
||||
Represents a single BUFR message.
|
||||
"""
|
||||
|
||||
def __repr__(self) -> str:
|
||||
"""Return a string representation of the message."""
|
||||
...
|
||||
|
||||
def version(self) -> int:
|
||||
"""
|
||||
Get the BUFR edition/version number.
|
||||
|
||||
Returns:
|
||||
int: BUFR edition (typically 2, 3, or 4)
|
||||
"""
|
||||
...
|
||||
|
||||
class BUFRParsed:
|
||||
"""
|
||||
Represents parsed BUFR data.
|
||||
|
||||
This class contains the decoded meteorological data from a BUFR message.
|
||||
"""
|
||||
|
||||
def __repr__(self) -> str:
|
||||
"""
|
||||
Return a formatted string representation of the parsed data.
|
||||
|
||||
Returns:
|
||||
str: Human-readable representation of all records
|
||||
"""
|
||||
...
|
||||
|
||||
def set_tables_path(path: str) -> None:
|
||||
"""
|
||||
Set the base path for BUFR table files.
|
||||
|
||||
This function configures where the decoder should look for BUFR table files
|
||||
(Table B, Table D, etc.) needed for decoding messages.
|
||||
|
||||
Args:
|
||||
path: Absolute path to the directory containing BUFR tables
|
||||
|
||||
Example:
|
||||
>>> import rbufrp
|
||||
>>> rbufrp.set_tables_path("/usr/share/bufr/tables")
|
||||
"""
|
||||
...
|
||||
|
||||
def get_tables_path() -> str:
|
||||
"""
|
||||
Get the currently configured base path for BUFR table files.
|
||||
|
||||
Returns:
|
||||
str: Current tables directory path
|
||||
|
||||
Example:
|
||||
>>> import rbufrp
|
||||
>>> print(rbufrp.get_tables_path())
|
||||
/usr/share/bufr/tables
|
||||
"""
|
||||
...
|
||||
|
||||
__all__ = [
|
||||
"BUFRDecoder",
|
||||
"BUFRFile",
|
||||
"BUFRMessage",
|
||||
"BUFRParsed",
|
||||
"set_tables_path",
|
||||
"get_tables_path",
|
||||
]
|
||||
|
||||
0
rbufrp/src/rbufrp/py.typed
Normal file
0
rbufrp/src/rbufrp/py.typed
Normal file
Loading…
Reference in New Issue
Block a user