Skip to content

Commit 38179ef

Browse files
committed
starlark: add APIs for Python resource data
We can now iterate resources in Python distributions and add resources to PythonEmbeddedResources instances.
1 parent c59d829 commit 38179ef

3 files changed

Lines changed: 122 additions & 2 deletions

File tree

docs/config.rst

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -143,6 +143,12 @@ Examples:
143143
Returns a ``list`` of ``PythonSourceModule`` representing Python
144144
source modules present in this distribution.
145145

146+
``PythonDistribution.resources_data()``
147+
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
148+
149+
Returns a ``list`` of ``PythonResourceData`` representing resource files
150+
present in this distribution.
151+
146152
``default_python_distribution(build_target=None)``
147153
--------------------------------------------------
148154

@@ -187,6 +193,21 @@ Each instance has the following attributes:
187193
``is_package`` (bool)
188194
Whether the module is also a Python package (or sub-package).
189195

196+
``PythonResourcesData``
197+
-----------------------
198+
199+
This type represents Python resource data. Resource data is a named
200+
blob associated with a Python package. It is typically accessed using
201+
the ``importlib.resources`` API.
202+
203+
Each instance has the following attributes:
204+
205+
``package`` (string)
206+
Python package this resource is associated with.
207+
208+
``name`` (string)
209+
Name of this resource.
210+
190211
``PythonEmbeddedResources()``
191212
-----------------------------
192213

@@ -220,6 +241,16 @@ or ``2``.
220241
Only one level of bytecode can be registered per named module. If called
221242
multiple times for the same module, the last write wins.
222243

244+
``PythonEmbeddedResources.add_resource_data(resource)``
245+
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
246+
247+
This method adds a ``PythonResourceData`` instance to the
248+
``PythonEmbeddedResources`` instance, making that resource available
249+
via in-memory access.
250+
251+
If multiple resources sharing the same ``(package, name)`` pair are added,
252+
the last added one is used.
253+
223254
.. _config_embedded_python_config:
224255

225256
``EmbeddedPythonConfig(...)```

pyoxidizer/src/starlark/python_distribution.rs

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ use std::collections::HashMap;
1919
use std::path::{Path, PathBuf};
2020

2121
use super::env::{optional_str_arg, required_str_arg};
22-
use super::python_resource::PythonSourceModule;
22+
use super::python_resource::{PythonResourceData, PythonSourceModule};
2323
use crate::app_packaging::environment::EnvironmentContext;
2424
use crate::py_packaging::distribution::{
2525
resolve_parsed_distribution, ParsedPythonDistribution, PythonDistributionLocation,
@@ -133,6 +133,20 @@ starlark_module! { python_distribution_module =>
133133
})))
134134
}
135135

136+
PythonDistribution.resources_data(env env, this) {
137+
let context = env.get("CONTEXT").expect("CONTEXT not defined");
138+
139+
let logger = context.downcast_apply(|x: &EnvironmentContext| x.logger.clone());
140+
141+
Ok(Value::from(this.downcast_apply_mut(|dist: &mut PythonDistribution| {
142+
dist.ensure_distribution_resolved(&logger);
143+
144+
dist.distribution.as_ref().unwrap().resources_data().iter().map(|data| {
145+
Value::new(PythonResourceData { data: data.clone() })
146+
}).collect_vec()
147+
})))
148+
}
149+
136150
default_python_distribution(env env, build_target=None) {
137151
let build_target = match build_target.get_type() {
138152
"NoneType" => env.get("BUILD_TARGET").unwrap().to_string(),

pyoxidizer/src/starlark/python_resource.rs

Lines changed: 76 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,9 @@ use std::collections::HashMap;
1717

1818
use super::env::required_type_arg;
1919
use crate::py_packaging::embedded_resource::EmbeddedPythonResourcesPrePackaged;
20-
use crate::py_packaging::resource::{BytecodeModule, BytecodeOptimizationLevel, SourceModule};
20+
use crate::py_packaging::resource::{
21+
BytecodeModule, BytecodeOptimizationLevel, ResourceData, SourceModule,
22+
};
2123

2224
#[derive(Debug, Clone)]
2325
pub struct PythonSourceModule {
@@ -150,6 +152,68 @@ impl TypedValue for PythonBytecodeModule {
150152
}
151153
}
152154

155+
#[derive(Debug, Clone)]
156+
pub struct PythonResourceData {
157+
pub data: ResourceData,
158+
}
159+
160+
impl TypedValue for PythonResourceData {
161+
immutable!();
162+
any!();
163+
not_supported!(
164+
binop, dir_attr, function, get_hash, indexable, iterable, sequence, set_attr, to_int
165+
);
166+
167+
fn to_str(&self) -> String {
168+
format!(
169+
"PythonResourceData<package={}, name={}>",
170+
self.data.package, self.data.name
171+
)
172+
}
173+
174+
fn to_repr(&self) -> String {
175+
self.to_str()
176+
}
177+
178+
fn get_type(&self) -> &'static str {
179+
"PythonResourceData"
180+
}
181+
182+
fn to_bool(&self) -> bool {
183+
true
184+
}
185+
186+
fn compare(&self, other: &dyn TypedValue, _recursion: u32) -> Result<Ordering, ValueError> {
187+
default_compare(self, other)
188+
}
189+
190+
fn get_attr(&self, attribute: &str) -> ValueResult {
191+
let v = match attribute {
192+
"package" => Value::new(self.data.package.clone()),
193+
"name" => Value::new(self.data.name.clone()),
194+
// TODO expose raw data
195+
attr => {
196+
return Err(ValueError::OperationNotSupported {
197+
op: format!(".{}", attr),
198+
left: "PythonResourceData".to_string(),
199+
right: None,
200+
})
201+
}
202+
};
203+
204+
Ok(v)
205+
}
206+
207+
fn has_attr(&self, attribute: &str) -> Result<bool, ValueError> {
208+
Ok(match attribute {
209+
"package" => true,
210+
"name" => true,
211+
// TODO expose raw data
212+
_ => false,
213+
})
214+
}
215+
}
216+
153217
#[derive(Debug, Clone)]
154218
pub struct PythonEmbeddedResources {
155219
pub embedded: EmbeddedPythonResourcesPrePackaged,
@@ -236,4 +300,15 @@ starlark_module! { python_resource_env =>
236300

237301
Ok(Value::new(None))
238302
}
303+
304+
PythonEmbeddedResources.add_resource_data(this, resource) {
305+
required_type_arg("resource", "PythonResourceData", &resource)?;
306+
307+
this.downcast_apply_mut(|embedded: &mut PythonEmbeddedResources| {
308+
let r = resource.downcast_apply(|r: &PythonResourceData| r.data.clone());
309+
embedded.embedded.add_resource(&r);
310+
});
311+
312+
Ok(Value::new(None))
313+
}
239314
}

0 commit comments

Comments
 (0)