feat!: merge 5.0.0 (#136)
- refactor!: replace Real and Integer with double and int (#131)
Replace the uses of ostk::core::type::Integer in the library, tests and Python bindings. The URL port was the only Real/Integer user in io. Its undefined state (Integer::Undefined(), meaning “no port in the URL”) has no faithful int value, so it becomes std::optional.
- URL port is now std::optional: constructor argument (default std::nullopt), accessPort(), getPort(), setPort() and the member
- URL::Parse parses the port with boost::lexical_cast and throws the same RuntimeError (“Cannot cast string [..] to Integer.”) as Integer::Parse did; no port gives std::nullopt
- toString() and operator<< print the port as before (“Undefined” when absent)
- operator== keeps its behaviour (two missing ports are equal); operator!= now returns true when only one URL has a port (Integer’s != returned false when either side was undefined, so it was not the negation of ==)
- Remove the ostk::io::Integer alias and the Core/Type/Integer.hpp include from URL.hpp
- Add C++ and Python tests for a missing port
Python-visible changes:
- URL.get_port() returns int, or None when the URL has no port (was ostk.core.type.Integer, undefined when there was no port)
- URL(…) and URL.set_port() take int or None for the port (None removes it); plain Python ints keep working
BREAKING CHANGE: URL(const String&, const String&, const String&, const Integer& = Integer::Undefined(), …) becomes URL(…, const std::optional& aPort = std::nullopt, …); const Integer& URL::accessPort() const becomes const std::optional& URL::accessPort() const; Integer URL::getPort() const becomes std::optional URL::getPort() const; void URL::setPort(const Integer&) becomes void URL::setPort(const std::optional&). The ostk::io::Integer alias is removed and URL.hpp no longer includes Core/Type/Integer.hpp. Python URL.get_port() returns int or None, and URL(…)/URL.set_port() take int or None.
Co-authored-by: Claude Opus 5.5 noreply@anthropic.com
refactor!: replace Undefined pattern with std::optional (#132)
refactor!: remove unimplemented Undefined declarations from ip::Address
ip::Address is declared but not implemented (Address.cpp is empty and the class has no Python binding). Like core’s System::User, System::Group and Logger::Source, drop its unimplemented null-object declarations so the Undefined pattern does not survive in the public header.
- Remove the Address::Undefined() and Address::isDefined() declarations
- Remove Address::Type::Undefined; Address::Type is now { IPv4, IPv6 }
BREAKING CHANGE: removed the declarations Address::Undefined, Address::isDefined and the enumerator Address::Type::Undefined. None of them had a definition, so no linked code can depend on them. There is no Python change.
Co-Authored-By: Claude Opus 5.5 noreply@anthropic.com
- refactor!: replace Undefined pattern with std::optional in http::Request
A Request is now always defined. Request::Method::Undefined was not an “unspecified” default (no signature defaulted to it); it only existed to build the Request::Undefined() null object, so it is removed with it. Callers that need “no request” should use std::optional.
- Remove Request::Undefined(), Request::isDefined() and Request::Method::Undefined
- Remove the Undefined(“Request”) guards from getMethod(), getUrl() and getBody(), and the Undefined entry from StringFromMethod()
- Client::Send no longer checks aRequest.isDefined(). A request whose URL is undefined still throws Undefined(“URL”) when Send serializes the URL (until URL itself is migrated)
- Python: remove Request.undefined(), Request.is_defined() and Request.Method.Undefined
Request::Method values shift down by one (Get is now 0). Nothing in this repository depends on the integer values: StringFromMethod uses a map and Client::Send uses StringFromMethod.
BREAKING CHANGE: removed Request::Undefined, Request::isDefined and Request::Method::Undefined. Use std::optional where a request may be absent, and pass a real method. Python: Request.undefined(), Request.is_defined() and Request.Method.Undefined are removed; use None for “no request”.
Co-Authored-By: Claude Opus 5.5 noreply@anthropic.com
- refactor!: replace Undefined pattern with std::optional in http::Response
A Response is now always defined. Response::StatusCode::Undefined (0) was not an “unspecified” default (no signature defaulted to it); it only marked the Response::Undefined() null object, so it is removed with it. Callers that need “no response” should use std::optional.
- Remove Response::Undefined(), Response::isDefined() and Response::StatusCode::Undefined
- Remove the Undefined(“Response”) guards from isOk(), getStatusCode() and getBody(), and the Undefined entry from StringFromStatusCode()
- Client::Send used to return an undefined response when cURL reported no status code (0, e.g. for file:// transfers); every accessor of that response then threw Undefined(“Response”). Send now throws that same exception directly
- Python: remove Response.undefined(), Response.is_defined() and Response.StatusCode.Undefined
The other StatusCode enumerators have explicit values, so their integer values do not change.
BREAKING CHANGE: removed Response::Undefined, Response::isDefined and Response::StatusCode::Undefined. Use std::optional where a response may be absent. Client::Send now throws ostk::core::error::runtime::Undefined(“Response”) when no status code is received, instead of returning an undefined response. Python: Response.undefined(), Response.is_defined() and Response.StatusCode.Undefined are removed; Client.send raises RuntimeError in the no-status-code case.
Co-Authored-By: Claude Opus 5.5 noreply@anthropic.com
- refactor!: replace Undefined pattern with std::optional in URL
A URL is now always defined. The undefined state (an empty scheme or host) is rejected when the URL is built or modified, instead of being carried around and rejected later by toString(), the comparison operators and http::Client. Callers that need “no URL” should use std::optional.
- Remove URL::Undefined() and URL::isDefined()
- The URL constructor, setScheme() and setHost() throw ostk::core::error::runtime::Undefined(“Scheme”) / (“Host”) for an empty scheme or host. URL::Parse is unchanged: it still throws Wrong for a string without scheme or host, before constructing the URL
- Remove the Undefined(“URL”) guard from toString(), the early returns for undefined operands in operator== and operator!=, and the “Undefined” fallbacks for scheme and host in operator<<
- Request’s operator<< prints the URL without an isDefined() check, and Client::Fetch and Client::List no longer check aUrl.isDefined(). Their core-owned Directory/File isDefined() checks are unchanged
- Python: remove URL.undefined() and URL.is_defined(); URL(…), set_scheme and set_host raise RuntimeError for an empty scheme or host
- Remove the URL.undefined() and url.is_defined() cells from the URL reference notebook
url::Query::Undefined() and Query::isDefined() are unchanged: an “undefined” query is the empty query, a valid value with ordinary equality and no guards.
BREAKING CHANGE: removed URL::Undefined and URL::isDefined. Use std::optional where a URL may be absent (for example a variable that is assigned later), and drop isDefined() checks on URLs. Constructing a URL with an empty scheme or host, or setting one, now throws ostk::core::error::runtime::Undefined instead of producing an undefined URL. Python: URL.undefined() and URL.is_defined() are removed; use None for “no URL”.
Co-Authored-By: Claude Opus 5.5 noreply@anthropic.com
Co-authored-by: Claude Opus 5.5 noreply@anthropic.com
- refactor!: normalize getters to return const references for stored members (#133)
Only getters are kept. Stored non-trivial members return
const T&so the caller decides whether to copy. Computed and trivial values return by value.
- URL: the const accessScheme/accessHost/accessPath/accessPort/accessUser/ accessPassword/accessQuery/accessFragment duplicates are removed; getScheme/getHost/getPath/getUser/getPassword/getFragment return
const String&, getPort returnsconst std::optional<int>&and getQuery returnsconst Query&- url::Query::Parameter: the const accessName/accessValue duplicates are removed; getName/getValue return
const String&- http::Request: getUrl returns
const URL&and getBody returnsconst String&- http::Response: getBody returns
const String&(with a&&overload returning a copy, soClient::Get(url).getBody()does not dangle)- Client::Send now passes a pointer into the request’s stored body to CURLOPT_POSTFIELDS instead of into a destroyed temporary copy
- Python: get_body on Response is bound through a lambda because of the ref-qualified overloads; Python names and return values are unchanged
BREAKING CHANGE: URL::accessScheme/accessHost/accessPath/accessPort/ accessUser/accessPassword/accessQuery/accessFragment and url::Query::Parameter::accessName/accessValue are removed (use the get* getters). URL::getScheme/getHost/getPath/getUser/getPassword/getFragment, Query::Parameter::getName/getValue and Request::getBody now return
const String&, URL::getPort returnsconst std::optional<int>&, URL::getQuery returnsconst Query&, Request::getUrl returnsconst URL&, and Response::getBody is ref-qualified (const String& getBody() const&,String getBody() &&), so member pointers to it are ambiguous.Co-authored-by: Claude Opus 5.5 noreply@anthropic.com
build!: migrate python bindings from pybind11 to nanobind (#134)
build!: adapt to open-space-toolkit-core v6
Move to open-space-toolkit-core 6 and adapt every call site to its API. No public API of this repository changes.
- CMake: FIND_PACKAGE (OpenSpaceToolkitCore 6). The development image now installs the latest core 6 packages (OSTK_CORE_MAJOR=”6”).
- Python: open-space-toolkit-core~=6.0 in pyproject.toml.in.
- Core’s Directory/File::Undefined() and isDefined() were removed (core 2b2ed644), so a Directory or File is always defined:
- Client::Fetch no longer checks aDirectory.isDefined(), and the File::Undefined() placeholder is gone: the destination File is now declared where it is built from the directory and the URL path.
- Client::List no longer checks aFile.isDefined().
- The Fetch test cases that passed Directory::Undefined() (and a commented-out List case that passed File::Undefined()) are removed.
Steps 1-3 of this release listed these as the core boundaries. The other core APIs io uses (Path, String::Format, Array range-for, File/Directory getPath, error::RuntimeError and error::runtime::Undefined) are unchanged in core 6, and io uses no Real, Integer or const access* from core.
BREAKING CHANGE: requires open-space-toolkit-core 6 (C++ and Python). Client::Fetch and Client::List no longer throw ostk::core::error::runtime::Undefined for an undefined Directory/File, because core 6 cannot construct one.
Co-Authored-By: Claude Opus 5.5 noreply@anthropic.com
- build!: migrate python bindings from pybind11 to nanobind
Follow open-space-toolkit-core v6.0.0 (ed59a510, nanobind v3.1.0) and move the Python bindings from pybind11 to nanobind. The build files mirror core’s: nanobind is fetched with FetchContent (pinned to v3.1.0, as core, so that both extension modules share one nanobind ABI and core’s types resolve here), its runtime is built once per Python version from nb_combined.cpp (NB_ADD_LIBRARY, with -fno-strict-aliasing, hidden visibility and NB_COMPACT_ASSERTIONS outside Debug), PY_STRIP replaces pybind11_strip, and stubs come from nanobind’s stubgen, written into the package directory so that the wheel ships them (with py.typed). No development image change is needed. The README dependency table lists nanobind instead of pybind11.
Python 3.9 is dropped (nanobind 3 and core 6 both require Python >= 3.10): PYTHON_SEARCH_VERSIONS is 3.10-3.14, requires-python is “>=3.10” and uv.lock is regenerated.
The bindings translate almost mechanically. The parts that do not:
pybind11/operators.h:URL,QueryandQuery.Parameter==/!=andURL + Stringare explicit lambdas bound to their dunder names withis_operator().URL += StringtakesURL&and returns it underrv_policy::none, soa += bkeeps returningaitself.- nanobind has no umbrella STL header.
Utilities/ArrayCasting.hppnow includes the nanobind STL casters (as core’s does;std::stringand thestd::optional<int>URL port need them) next to theArray<T>list caster, and is included once at the top of the module, before any binding that uses those casters."doSanitize"_a = falseneedsusing namespace nanobind::literals.- pybind11 set
__hash__ = Noneon classes that define__eq__(as Python does for class bodies); nanobind keeps the identity hash. The module now restores the pybind11 rule for its own classes at import (Utilities/Hash.hpp, the same helper as mathematics), soURL,QueryandQuery.Parameterstay unhashable.No
init(factory),make_iterator,arg_v,handle.cast, trampoline, holder type, custom exception,implicitly_convertible, Eigen or NumPy is used in this module. Theconst&getters (normalised in the previous commits) copy under nanobind’s default policy, as under pybind11, andResponse.get_bodystays a by-value lambda over the ref-qualified overloads.Verification: the 12 existing Python tests pass unchanged. New tests pin
URL +=returning the same object and the unhashable classes, and test/test_cross_module.py passes core v6 objects into and out of this module (String arguments, the str -> String implicit conversion registered by core, String return values, a Directory argument and a File return value through Client.fetch on a file:// URL, and a File argument to Client.list). All 19 pass on both the pybind11 build and this one. dir() of every class is unchanged apart from the pybind11-only_pybind11_conduit_v1_attribute and the enums (below). Importing and exercising the module reports no nanobind leaks at shutdown. On Python 3.11 the stripped extension module is 15% smaller (328 kB -> 280 kB), and a micro-benchmark of ten call shapes (method calls, ==, parse, construction, operators, enum and list arguments) is 3.3x faster by geometric mean (1.2x to 6.5x).User-visible behaviour changes:
Request.MethodandResponse.StatusCodeare Pythonenum.Enumsubclasses, as in core v6:int(member)raises TypeError (use.value),repr(member)isMethod.Getinstead of<Method.Get: 0>, and__members__is a mappingproxy instead of a dict..name,.value, member order, equality, hashing andMethod(0)are unchanged. An enum parameter now also accepts the plain int value (Request(0, url, "")), which pybind11 rejected with TypeError.- Error messages for failed argument conversions follow nanobind’s wording (“incompatible function arguments”, unqualified type names such as
URLinstead ofostk.io.URL); the exception types are unchanged (TypeError). No io binding takes a C++char, so core’s ValueError -> TypeError change has no counterpart here.Not addressed:
Utilities/IterableConverter.hpp(Boost.Python, never included) is left as it was.URL.set_array(a no-op free function bound as a method, so it is only callable asURL.set_array([...])) is kept as is; its stub now readsset_array(self).- The
BUILD_WHEELS_IN_CMAKE=OFFbranch still generates no stubs (as in core).- nanobind’s own runtime sources trigger -Wshadow warnings under this project’s flags; the binding sources build warning-free.
BREAKING CHANGE: Python 3.9 is no longer supported; enums are enum.Enum (no int()).
Co-Authored-By: Claude Opus 5.5 noreply@anthropic.com
Co-authored-by: Claude Opus 5.5 noreply@anthropic.com
Co-authored-by: Claude Opus 5.5 noreply@anthropic.com
Open Space Toolkit IO 组件(YAML/JSON 等数据序列化)。镜像收录自 https://github.com/open-space-collective/open-space-toolkit-io,License:Apache-2.0
版权所有:中国计算机学会技术支持:开源发展技术委员会
京ICP备13000930号-9
京公网安备 11010802047560号
Open Space Toolkit ▸ I/O
Addressing, networking, database connectors.
Getting Started
Want to get started? This is the simplest and quickest way:
Nothing to download or install! This will automatically start a JupyterLab environment in your browser with Open Space Toolkit libraries and example notebooks ready to use.
Alternatives
Docker Images
Docker must be installed on your system.
iPython
The following command will start an iPython shell within a container where the OSTk components are already installed:
Once the shell is up and running, playing with it is easy:
Tip: Use tab for auto-completion!
JupyterLab
The following command will start a JupyterLab server within a container where the OSTk components are already installed:
Once the container is running, access http://localhost:8888/lab and create a Python 3 Notebook.
Installation
C++
The binary packages are hosted using GitHub Releases:
open-space-toolkit-io-X.Y.Z-1.x86_64-runtimeopen-space-toolkit-io-X.Y.Z-1.x86_64-developen-space-toolkit-io-X.Y.Z-1.x86_64-pythonDebian / Ubuntu
After downloading the relevant
.debbinary packages, install:Python
Install from PyPI:
Documentation
Documentation is available here:
Structure
The library exhibits the following structure:
Tutorials
Tutorials are available here:
Setup
Development Environment
Using Docker for development is recommended, to simplify the installation of the necessary build tools and dependencies. Instructions on how to install Docker are available here.
To start the development environment:
This will:
openspacecollective/open-space-toolkit-io-developmentDocker image.bashshell from the./buildworking directory.If installing Docker is not an option, you can manually install the development tools (GCC, CMake) and all required dependencies, by following a procedure similar to the one described in the Development Dockerfile.
Build
From the
./builddirectory:Tip:
ostk-buildsimplifies building from within the development environment.Test
To start a container to build and run the tests:
Or to run them manually:
Tip:
ostk-testsimplifies running tests from within the development environment.Dependencies
3.1.05.2.0mainContribution
Contributions are more than welcome!
Please read our contributing guide to learn about our development process, how to propose fixes and improvements, and how to build and test the code.
Special Thanks
License
Apache License 2.0