Skip to content

Commit

Permalink
feat: add RunExportsJson (conda#566)
Browse files Browse the repository at this point in the history
  • Loading branch information
Wackyator committed Mar 19, 2024
1 parent 047e4a4 commit 0e2a5f9
Show file tree
Hide file tree
Showing 13 changed files with 472 additions and 102 deletions.
212 changes: 117 additions & 95 deletions py-rattler/Cargo.lock

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions py-rattler/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ rattler_solve = { path = "../crates/rattler_solve", default-features = false, fe
] }
rattler_index = { path = "../crates/rattler_index" }
rattler_lock = { path = "../crates/rattler_lock", default-features = false }
rattler_package_streaming = { path = "../crates/rattler_package_streaming", default-features = false }

pyo3 = { version = "0.19", features = [
"abi3-py38",
Expand Down
1 change: 0 additions & 1 deletion py-rattler/docs/about_json.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
# AboutJson

::: rattler.package.about_json

4 changes: 4 additions & 0 deletions py-rattler/docs/run_exports_json.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@

# RunExportsJson

::: rattler.package.run_exports_json
1 change: 1 addition & 0 deletions py-rattler/mkdocs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ nav:
- PypiPackageEnvironmentData: pypi_package_environment_data.md
- metadata:
- AboutJson: about_json.md
- RunExportsJson: run_exports_json.md
- match_spec:
- MatchSpec: match_spec.md
- NamelessMatchSpec: nameless_match_spec.md
Expand Down
3 changes: 2 additions & 1 deletion py-rattler/rattler/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
from rattler.channel import Channel, ChannelConfig
from rattler.networking import AuthenticatedClient, fetch_repo_data
from rattler.virtual_package import GenericVirtualPackage, VirtualPackage
from rattler.package import PackageName, AboutJson
from rattler.package import PackageName, AboutJson, RunExportsJson
from rattler.prefix import PrefixRecord, PrefixPaths
from rattler.solver import solve
from rattler.platform import Platform
Expand Down Expand Up @@ -61,4 +61,5 @@
"link",
"index",
"AboutJson",
"RunExportsJson",
]
5 changes: 5 additions & 0 deletions py-rattler/rattler/exceptions.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
ConvertSubdirError,
VersionBumpError,
EnvironmentCreationError,
ExtractError,
)
except ImportError:
# They are only redefined for documentation purposes
Expand Down Expand Up @@ -77,6 +78,9 @@ class VersionBumpError(Exception): # type: ignore[no-redef]
class EnvironmentCreationError(Exception): # type: ignore[no-redef]
"""An error that can occur when creating an environment."""

class ExtractError(Exception): # type: ignore[no-redef]
"""An error that can occur when extracting an archive."""


__all__ = [
"ActivationError",
Expand All @@ -97,4 +101,5 @@ class EnvironmentCreationError(Exception): # type: ignore[no-redef]
"ConvertSubdirError",
"VersionBumpError",
"EnvironmentCreationError",
"ExtractError",
]
3 changes: 2 additions & 1 deletion py-rattler/rattler/package/__init__.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
from rattler.package.package_name import PackageName
from rattler.package.about_json import AboutJson
from rattler.package.run_exports_json import RunExportsJson

__all__ = ["PackageName", "AboutJson"]
__all__ = ["PackageName", "AboutJson", "RunExportsJson"]
200 changes: 200 additions & 0 deletions py-rattler/rattler/package/run_exports_json.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,200 @@
from __future__ import annotations
import os
from pathlib import Path
from typing import List

from rattler.rattler import PyRunExportsJson


class RunExportsJson:
"""
A representation of the `run_exports.json` file found in package archives.
The `run_exports.json` file contains information about the run exports of a package
"""

_inner: PyRunExportsJson

@staticmethod
def from_package_archive(path: os.PathLike[str]) -> RunExportsJson:
"""
Parses the package file from archive.
Note: If you want to extract multiple `info/*` files then this will be slightly
slower than manually iterating over the archive entries with
custom logic as this skips over the rest of the archive
Examples
--------
```python
>>> run_exports = RunExportsJson.from_package_archive(
... "../test-data/with-symlinks/python-3.10.6-h2c4edbf_0_cpython.tar.bz2"
... )
>>> run_exports
RunExportsJson()
>>>
```
"""
return RunExportsJson._from_py_run_exports_json(
PyRunExportsJson.from_package_archive(path)
)

@staticmethod
def from_path(path: os.PathLike[str]) -> RunExportsJson:
"""
Parses the object from a file specified by a `path`, using a format
appropriate for the file type.
For example, if the file is in JSON format, this function reads the data
from the file at the specified path, parse the JSON string and return the
resulting object. If the file is not in a parsable format or if the file
could not read, this function returns an error.
"""
return RunExportsJson._from_py_run_exports_json(
PyRunExportsJson.from_path(Path(path))
)

@staticmethod
def from_package_directory(path: os.PathLike[str]) -> RunExportsJson:
"""
Parses the object by looking up the appropriate file from the root of the
specified Conda archive directory, using a format appropriate for the file
type.
For example, if the file is in JSON format, this function reads the
appropriate file from the archive, parse the JSON string and return the
resulting object. If the file is not in a parsable format or if the file
could not be read, this function returns an error.
"""
return RunExportsJson._from_py_run_exports_json(
PyRunExportsJson.from_package_directory(Path(path))
)

@staticmethod
def from_str(string: str) -> RunExportsJson:
"""
Parses the object from a string, using a format appropriate for the file
type.
For example, if the file is in JSON format, this function parses the JSON
string and returns the resulting object. If the file is not in a parsable
format, this function returns an error.
"""
return RunExportsJson._from_py_run_exports_json(
PyRunExportsJson.from_str(string)
)

@staticmethod
def package_path() -> str:
"""
Returns the path to the file within the Conda archive.
The path is relative to the root of the archive and includes any necessary
directories.
"""
return PyRunExportsJson.package_path()

@property
def weak(self) -> List[str]:
"""
Weak run exports apply a dependency from host to run.
Examples
--------
```python
>>> run_exports = RunExportsJson.from_package_archive(
... "../test-data/with-symlinks/python-3.10.6-h2c4edbf_0_cpython.tar.bz2"
... )
>>> run_exports.weak
['python_abi 3.10.* *_cp310']
>>>
```
"""
return self._inner.weak

@property
def strong(self) -> List[str]:
"""
Strong run exports apply a dependency from build to host and run.
Examples
--------
```python
>>> run_exports = RunExportsJson.from_package_archive(
... "../test-data/with-symlinks/python-3.10.6-h2c4edbf_0_cpython.tar.bz2"
... )
>>> run_exports.strong
[]
>>>
```
"""
return self._inner.strong

@property
def noarch(self) -> List[str]:
"""
NoArch run exports apply a run export only to noarch packages (other run exports are ignored).
For example, python uses this to apply a dependency on python to all noarch packages, but not to
the python_abi package.
Examples
--------
```python
>>> run_exports = RunExportsJson.from_package_archive(
... "../test-data/with-symlinks/python-3.10.6-h2c4edbf_0_cpython.tar.bz2"
... )
>>> run_exports.noarch
['python']
>>>
```
"""
return self._inner.noarch

@property
def weak_constrains(self) -> List[str]:
"""
Weak constrains apply a constrain dependency from host to build, or run to host.
Examples
--------
```python
>>> run_exports = RunExportsJson.from_package_archive(
... "../test-data/with-symlinks/python-3.10.6-h2c4edbf_0_cpython.tar.bz2"
... )
>>> run_exports.weak_constrains
[]
>>>
```
"""
return self._inner.weak_constrains

@property
def strong_constrains(self) -> List[str]:
"""
Strong constrains apply a constrain dependency from build to host and run.
Examples
--------
```python
>>> run_exports = RunExportsJson.from_package_archive(
... "../test-data/with-symlinks/python-3.10.6-h2c4edbf_0_cpython.tar.bz2"
... )
>>> run_exports.strong_constrains
[]
>>>
```
"""
return self._inner.strong_constrains

@classmethod
def _from_py_run_exports_json(
cls, py_run_exports_json: PyRunExportsJson
) -> RunExportsJson:
run_exports_json = cls.__new__(cls)
run_exports_json._inner = py_run_exports_json

return run_exports_json

def __repr__(self) -> str:
"""
Returns a representation of the RunExportsJson.
"""
return "RunExportsJson()"
5 changes: 5 additions & 0 deletions py-rattler/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ use rattler_conda_types::{
ParseMatchSpecError, ParsePlatformError, ParseVersionError, VersionBumpError,
};
use rattler_lock::{ConversionError, ParseCondaLockError};
use rattler_package_streaming::ExtractError;
use rattler_repodata_gateway::fetch::FetchRepoDataError;
use rattler_shell::activation::ActivationError;
use rattler_solve::SolveError;
Expand Down Expand Up @@ -59,6 +60,8 @@ pub enum PyRattlerError {
RequirementError(String),
#[error("{0}")]
EnvironmentCreationError(String),
#[error(transparent)]
ExtractError(#[from] ExtractError),
}

impl From<PyRattlerError> for PyErr {
Expand Down Expand Up @@ -105,6 +108,7 @@ impl From<PyRattlerError> for PyErr {
PyRattlerError::EnvironmentCreationError(err) => {
EnvironmentCreationException::new_err(err)
}
PyRattlerError::ExtractError(err) => ExtractException::new_err(err.to_string()),
}
}
}
Expand All @@ -130,3 +134,4 @@ create_exception!(exceptions, ParseCondaLockException, PyException);
create_exception!(exceptions, ConversionException, PyException);
create_exception!(exceptions, RequirementException, PyException);
create_exception!(exceptions, EnvironmentCreationException, PyException);
create_exception!(exceptions, ExtractException, PyException);
13 changes: 10 additions & 3 deletions py-rattler/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,13 +19,14 @@ mod solver;
mod version;
mod virtual_package;

mod run_exports_json;
use about_json::PyAboutJson;
use channel::{PyChannel, PyChannelConfig};
use error::{
ActivationException, CacheDirException, ConvertSubdirException, DetectVirtualPackageException,
EnvironmentCreationException, FetchRepoDataException, InvalidChannelException,
InvalidMatchSpecException, InvalidPackageNameException, InvalidUrlException,
InvalidVersionException, IoException, LinkException, ParseArchException,
EnvironmentCreationException, ExtractException, FetchRepoDataException,
InvalidChannelException, InvalidMatchSpecException, InvalidPackageNameException,
InvalidUrlException, InvalidVersionException, IoException, LinkException, ParseArchException,
ParsePlatformException, PyRattlerError, SolverException, TransactionException,
VersionBumpException,
};
Expand All @@ -40,6 +41,7 @@ use networking::{authenticated_client::PyAuthenticatedClient, py_fetch_repo_data
use package_name::PyPackageName;
use prefix_paths::PyPrefixPaths;
use repo_data::{patch_instructions::PyPatchInstructions, sparse::PySparseRepoData, PyRepoData};
use run_exports_json::PyRunExportsJson;
use version::PyVersion;

use pyo3::prelude::*;
Expand Down Expand Up @@ -97,6 +99,8 @@ fn rattler(py: Python<'_>, m: &PyModule) -> PyResult<()> {

m.add_class::<PyAboutJson>().unwrap();

m.add_class::<PyRunExportsJson>().unwrap();

m.add_function(wrap_pyfunction!(py_solve, m).unwrap())
.unwrap();
m.add_function(wrap_pyfunction!(get_rattler_version, m).unwrap())
Expand Down Expand Up @@ -170,5 +174,8 @@ fn rattler(py: Python<'_>, m: &PyModule) -> PyResult<()> {
)
.unwrap();

m.add("ExtractError", py.get_type::<ExtractException>())
.unwrap();

Ok(())
}
6 changes: 5 additions & 1 deletion py-rattler/src/lock/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -476,7 +476,11 @@ impl PyPypiPackageEnvironmentData {
/// The extras enabled for the package. Note that the order doesn't matter.
#[getter]
pub fn extras(&self) -> BTreeSet<String> {
self.inner.extras.iter().map(|e| e.to_string()).collect()
self.inner
.extras
.iter()
.map(std::string::ToString::to_string)
.collect()
}
}

Expand Down
Loading

0 comments on commit 0e2a5f9

Please sign in to comment.