Skip to content

Commit 8b7795d

Browse files
committed
docs: update installation instructions and enhance access control tests
1 parent 067459d commit 8b7795d

File tree

10 files changed

+416
-167
lines changed

10 files changed

+416
-167
lines changed

docs/contributing.md

Lines changed: 24 additions & 115 deletions
Original file line numberDiff line numberDiff line change
@@ -8,18 +8,31 @@ Welcome to the python-common-expression-language development guide! This documen
88

99
This Python package provides bindings for Google's Common Expression Language (CEL) using a Rust backend:
1010

11-
```
12-
┌─────────────────┐ ┌──────────────────┐ ┌─────────────────┐
13-
│ Python API │───▶│ Rust Wrapper │───▶│ cel crate │
14-
│ │ │ (PyO3) │ │ (upstream) │
15-
├─────────────────┤ ├──────────────────┤ ├─────────────────┤
16-
│ • cel.evaluate │ │ • Type conversion│ │ • CEL parser │
17-
│ • Context class │ │ • Error handling │ │ • Expression │
18-
│ • CLI tool │ │ • Function calls │ │ evaluation │
19-
└─────────────────┘ └──────────────────┘ └─────────────────┘
11+
```mermaid
12+
%%{init: {"flowchart": {"padding": 20}}}%%
13+
flowchart LR
14+
subgraph Python["  🐍 Python Layer  "]
15+
API["&nbsp;&nbsp;cel.evaluate()<br/>&nbsp;&nbsp;Context class<br/>&nbsp;&nbsp;CLI tool&nbsp;&nbsp;"]
16+
end
17+
18+
subgraph Rust["&nbsp;&nbsp;🦀 Rust Wrapper (PyO3)&nbsp;&nbsp;"]
19+
Wrapper["&nbsp;&nbsp;Type conversion<br/>&nbsp;&nbsp;Error handling<br/>&nbsp;&nbsp;Function calls&nbsp;&nbsp;"]
20+
end
21+
22+
subgraph CEL["&nbsp;&nbsp;⚡ CEL Engine (upstream)&nbsp;&nbsp;"]
23+
Engine["&nbsp;&nbsp;CEL parser<br/>&nbsp;&nbsp;Expression evaluation<br/>&nbsp;&nbsp;Built-in functions&nbsp;&nbsp;"]
24+
end
25+
26+
Python --> Rust
27+
Rust --> CEL
28+
29+
style Python fill:#e8f4f8,color:#2c3e50
30+
style Rust fill:#fdf2e9,color:#2c3e50
31+
style CEL fill:#f0f9ff,color:#2c3e50
2032
```
2133

2234
**Key Files:**
35+
2336
- `src/lib.rs` - Main evaluation engine and type conversions
2437
- `src/context.rs` - Context management and Python function integration
2538
- `python/cel/` - Python module structure and CLI
@@ -85,12 +98,6 @@ uv run pytest tests/test_upstream_improvements.py # Future compatibility
8598
uv run pytest --cov=cel
8699
```
87100

88-
**Test Categories:**
89-
- **Basic Operations** (42 tests) - Core CEL evaluation
90-
- **Arithmetic** (31 tests) - Math operations and mixed types
91-
- **Type Conversion** (23 tests) - Python ↔ CEL type mapping
92-
- **Context Management** (11 tests) - Variables and functions
93-
- **Upstream Detection** (26 tests) - Future compatibility monitoring
94101

95102
## Upstream Compatibility Strategy
96103

@@ -160,12 +167,6 @@ When updating the `cel` crate dependency:
160167
5. **Update documentation** to reflect new features
161168
6. **Test thoroughly** to ensure no regressions
162169

163-
Example recent upgrade (cel-interpreter 0.10.0 → cel 0.11.0):
164-
- Crate was renamed from `cel-interpreter` to `cel`
165-
- Function registration API completely changed (new `IntoFunction` trait)
166-
- All Python API remained backward compatible
167-
- 287 tests continued passing after migration
168-
169170
## Code Style & Conventions
170171

171172
### Rust Code
@@ -209,76 +210,6 @@ def add_function(self, name: str, func: Callable) -> None:
209210
"""
210211
```
211212

212-
### Testing Conventions
213-
214-
```python
215-
import pytest
216-
import cel
217-
218-
class TestFeatureCategory:
219-
"""Test [specific feature] with [scope] coverage."""
220-
221-
def test_specific_behavior(self):
222-
"""Test [what] [under what conditions]."""
223-
# Arrange
224-
context = {"key": "value"}
225-
226-
# Act
227-
result = cel.evaluate("key", context)
228-
229-
# Assert
230-
assert result == "value"
231-
232-
def test_error_condition(self):
233-
"""Test that [condition] raises [exception type]."""
234-
with pytest.raises(RuntimeError, match="Undefined variable"):
235-
cel.evaluate("undefined_variable")
236-
```
237-
238-
## Contributing Guidelines
239-
240-
### Development Process
241-
242-
1. **Issue Discussion** - Open an issue to discuss significant changes
243-
2. **Branch Creation** - Create feature branch from main
244-
3. **Implementation** - Follow code style and add tests
245-
4. **Testing** - Ensure all tests pass (`uv run pytest`)
246-
5. **Documentation** - Update docs for user-facing changes
247-
6. **Pull Request** - Submit with clear description and examples
248-
249-
### What We're Looking For
250-
251-
**High Priority Contributions:**
252-
- **Enhanced error handling** - Better Python exception mapping
253-
- **Performance improvements** - Optimization of type conversions
254-
- **Local utility functions** - Python implementations of missing CEL functions
255-
- **Documentation improvements** - Examples, guides, edge cases
256-
257-
**Upstream Contributions (cel crate):**
258-
- **String utilities** - `lowerAscii`, `upperAscii`, `indexOf`, etc.
259-
- **Type introspection** - `type()` function implementation
260-
- **Mixed arithmetic** - Better signed/unsigned integer support
261-
- **CEL spec compliance** - OR operator boolean return values
262-
263-
### Testing Requirements
264-
265-
All contributions must include:
266-
- **Unit tests** for new functionality
267-
- **Integration tests** for user-facing features
268-
- **Error condition tests** for edge cases
269-
- **Documentation tests** for examples in docs
270-
271-
```bash
272-
# Full test suite (required before PR)
273-
uv run pytest
274-
275-
# Documentation examples (must pass)
276-
uv run --group docs pytest tests/test_docs.py
277-
278-
# Upstream compatibility (monitoring)
279-
uv run pytest tests/test_upstream_improvements.py
280-
```
281-
282213
## Debugging & Troubleshooting
283214

284215
### Common Issues
@@ -321,28 +252,6 @@ uv run pytest --profile tests/test_performance.py
321252
## Release Process
322253

323254
1. **Version Bump** - Update version in `pyproject.toml`
324-
2. **Changelog** - Document changes in `CHANGELOG.md`
325-
3. **Testing** - Full test suite across Python versions
326-
4. **Documentation** - Update any version-specific docs
327-
5. **Release** - Tag and publish to PyPI via CI
328-
329-
## Resources
330-
331-
### Documentation
332-
- **User Docs**: https://python-common-expression-language.readthedocs.io/
333-
- **CEL Specification**: https://github.com/google/cel-spec
334-
- **cel crate**: https://docs.rs/cel/latest/cel/
335-
336-
### Development Tools
337-
- **PyO3 Guide**: https://pyo3.rs/
338-
- **maturin**: https://www.maturin.rs/
339-
- **Rust Book**: https://doc.rust-lang.org/book/
340-
341-
### Community
342-
- **Issues**: https://github.com/hardbyte/python-common-expression-language/issues
343-
- **Discussions**: Use GitHub Discussions for questions and ideas
344-
- **CEL Community**: https://github.com/google/cel-spec/discussions
345-
346-
---
255+
2. **Changelog** - Document changes in `CHANGELOG.md`
256+
3. **Release** - Create a release in GitHub to trigger publishing to PyPI
347257

348-
Thank you for contributing to python-common-expression-language! Your efforts help provide a robust, performant CEL implementation for the Python ecosystem.

docs/getting-started/installation.md

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -9,26 +9,27 @@ Getting Python CEL up and running is quick and easy.
99

1010
## Install from PyPI
1111

12-
=== "pip"
12+
=== "uv"
1313

1414
```bash
15-
pip install common-expression-language
15+
uv add common-expression-language
1616
```
1717

18-
=== "uv"
18+
=== "uv tool (CLI only)"
1919

20+
Install the CLI tool globally:
21+
2022
```bash
21-
uv add common-expression-language
23+
uv tool install common-expression-language
2224
```
2325

24-
=== "pipx (CLI only)"
26+
=== "pip"
2527

26-
If you only want the CLI tool:
27-
2828
```bash
29-
pipx install common-expression-language
29+
pip install common-expression-language
3030
```
3131

32+
3233
## Verify Installation
3334

3435
After installation, you should have both the Python library and CLI tool available:

docs/how-to-guides/access-control-policies.md

Lines changed: 94 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -126,10 +126,40 @@ def check_hierarchical_access(user, resource, action):
126126
"user": {**user, "role_level": role_hierarchy.get(user["role"], 0)},
127127
"resource": resource,
128128
"action": action,
129-
"required_level": 1 # Minimum level to access system
129+
"required_level": 0 # Minimum level to access system
130130
}
131131

132132
return evaluate(policy, context)
133+
134+
# Test the hierarchical access control
135+
guest_user = {"role": "guest", "id": "guest1"}
136+
user_account = {"role": "user", "id": "user1"}
137+
manager_account = {"role": "manager", "id": "mgr1"}
138+
139+
public_resource = {"public": True, "owner": "admin", "collaborators": []}
140+
private_resource = {"public": False, "owner": "user1", "collaborators": ["guest1"]}
141+
142+
# Test 1: Guest accessing public resource
143+
result = check_hierarchical_access(guest_user, public_resource, "read")
144+
assert result == True, "Guest should access public resource"
145+
146+
# Test 2: Guest accessing private resource (denied)
147+
result = check_hierarchical_access(guest_user, private_resource, "write")
148+
assert result == False, "Guest should not write to private resource"
149+
150+
# Test 3: User accessing owned resource
151+
result = check_hierarchical_access(user_account, private_resource, "write")
152+
assert result == True, "User should access owned resource"
153+
154+
# Test 4: Manager can delete (role_level >= 3)
155+
result = check_hierarchical_access(manager_account, private_resource, "delete")
156+
assert result == True, "Manager should delete any resource"
157+
158+
# Test 5: Guest as collaborator can read
159+
result = check_hierarchical_access(guest_user, private_resource, "read")
160+
assert result == True, "Guest collaborator should read resource"
161+
162+
print("✓ Hierarchical access control working correctly")
133163
```
134164

135165
### Time-Based Access
@@ -162,6 +192,33 @@ def check_time_based_access(user, resource, action, current_time=None):
162192
}
163193

164194
return evaluate(policy, context)
195+
196+
# Test time-based access control
197+
standard_user = {"role": "user", "schedule": "standard"}
198+
flexible_user = {"role": "user", "schedule": "flexible"}
199+
admin_user = {"role": "admin", "schedule": "standard"}
200+
test_resource = {"id": "test_doc"}
201+
202+
# Test 1: Standard user during business hours
203+
business_time = datetime.now().replace(hour=14) # 2 PM
204+
result = check_time_based_access(standard_user, test_resource, "read", business_time)
205+
assert result == True, "Standard user should access during business hours"
206+
207+
# Test 2: Standard user after hours (denied)
208+
after_hours = datetime.now().replace(hour=22) # 10 PM
209+
result = check_time_based_access(standard_user, test_resource, "read", after_hours)
210+
assert result == False, "Standard user should be denied after hours"
211+
212+
# Test 3: Flexible user during extended hours
213+
result = check_time_based_access(flexible_user, test_resource, "read", after_hours)
214+
assert result == True, "Flexible user should access during extended hours"
215+
216+
# Test 4: Admin always has access
217+
early_morning = datetime.now().replace(hour=5) # 5 AM
218+
result = check_time_based_access(admin_user, test_resource, "read", early_morning)
219+
assert result == True, "Admin should always have access"
220+
221+
print("✓ Time-based access control working correctly")
165222
```
166223

167224
### Resource-Specific Policies
@@ -200,6 +257,42 @@ def check_resource_specific_access(user, resource, action):
200257
}
201258

202259
return evaluate(policy, context)
260+
261+
# Test resource-specific access control
262+
developer = {"role": "developer", "id": "dev1"}
263+
analyst = {"role": "analyst", "id": "analyst1"}
264+
operator = {"role": "operator", "id": "ops1"}
265+
266+
document_resource = {"type": "document", "owner": "dev1", "public": False, "collaborators": ["analyst1"]}
267+
database_resource = {"type": "database", "name": "prod_db"}
268+
system_resource = {"type": "system", "name": "web_server"}
269+
270+
# Test 1: Developer with database (can read/write)
271+
result = check_resource_specific_access(developer, database_resource, "write")
272+
assert result == True, "Developer should write to database"
273+
274+
result = check_resource_specific_access(developer, database_resource, "read")
275+
assert result == True, "Developer should read database"
276+
277+
# Test 2: Analyst with database (read-only)
278+
result = check_resource_specific_access(analyst, database_resource, "read")
279+
assert result == True, "Analyst should read database"
280+
281+
result = check_resource_specific_access(analyst, database_resource, "write")
282+
assert result == False, "Analyst should not write to database"
283+
284+
# Test 3: Operator with system (can read/restart)
285+
result = check_resource_specific_access(operator, system_resource, "restart")
286+
assert result == True, "Operator should restart system"
287+
288+
# Test 4: Analyst as document collaborator
289+
result = check_resource_specific_access(analyst, document_resource, "read")
290+
assert result == True, "Analyst collaborator should read document"
291+
292+
result = check_resource_specific_access(analyst, document_resource, "write")
293+
assert result == False, "Analyst collaborator should not write document"
294+
295+
print("✓ Resource-specific access control working correctly")
203296
```
204297

205298
## Kubernetes Validation Rules

0 commit comments

Comments
 (0)