0
# Field Initialization Utilities
1
2
Convenience decorators for automatically copying constructor arguments to instance fields, reducing boilerplate code in class initializers by eliminating manual field assignment.
3
4
## Capabilities
5
6
### Internal Field Copying
7
8
Automatically copies constructor arguments to private instance fields with underscore prefix.
9
10
```python { .api }
11
def copy_args_to_internal_fields(fn):
12
"""
13
Decorator that copies __init__ arguments to internal (private) fields.
14
15
Applies to __init__ methods only. Each argument 'arg_name' becomes '_arg_name' field.
16
Cannot be used with *args parameters.
17
18
Parameters:
19
- fn: __init__ method to decorate
20
21
Returns:
22
Decorated __init__ method that copies args to _field_name attributes
23
"""
24
```
25
26
### Public Field Copying
27
28
Automatically copies constructor arguments to public instance fields with same names.
29
30
```python { .api }
31
def copy_args_to_public_fields(fn):
32
"""
33
Decorator that copies __init__ arguments to public fields.
34
35
Applies to __init__ methods only. Each argument 'arg_name' becomes 'arg_name' field.
36
Cannot be used with *args parameters.
37
38
Parameters:
39
- fn: __init__ method to decorate
40
41
Returns:
42
Decorated __init__ method that copies args to field_name attributes
43
"""
44
```
45
46
## Usage Examples
47
48
### Internal Fields with Dependency Injection
49
50
```python
51
import pinject
52
53
class DatabaseConfig(object):
54
def __init__(self):
55
self.host = "localhost"
56
self.port = 5432
57
58
class UserRepository(object):
59
@pinject.copy_args_to_internal_fields
60
def __init__(self, database_config, table_name="users"):
61
# Arguments automatically copied to _database_config and _table_name
62
pass
63
64
def get_connection_info(self):
65
return f"Connecting to {self._database_config.host}:{self._database_config.port}"
66
67
def get_table(self):
68
return self._table_name
69
70
obj_graph = pinject.new_object_graph()
71
repo = obj_graph.provide(UserRepository) # uses default table_name="users"
72
73
print(repo.get_connection_info()) # "Connecting to localhost:5432"
74
print(repo.get_table()) # "users"
75
print(hasattr(repo, 'database_config')) # False (not public)
76
print(hasattr(repo, '_database_config')) # True (internal field)
77
```
78
79
### Public Fields with Manual Logic
80
81
```python
82
import pinject
83
84
class Logger(object):
85
def __init__(self):
86
self.level = "INFO"
87
88
class FileService(object):
89
@pinject.copy_args_to_public_fields
90
def __init__(self, logger, base_path="/tmp"):
91
# Arguments automatically copied to .logger and .base_path
92
# Additional initialization logic can follow
93
self.files_processed = 0
94
print(f"FileService initialized with path {self.base_path}")
95
96
def process_file(self, filename):
97
self.logger.level = "DEBUG"
98
full_path = f"{self.base_path}/{filename}"
99
self.files_processed += 1
100
return full_path
101
102
obj_graph = pinject.new_object_graph()
103
service = obj_graph.provide(FileService) # uses default base_path="/tmp"
104
105
print(service.logger.level) # "INFO" (initially)
106
path = service.process_file("test.txt")
107
print(path) # "/tmp/test.txt"
108
print(service.logger.level) # "DEBUG" (modified)
109
print(service.files_processed) # 1
110
```
111
112
### Combined with Other Decorators
113
114
```python
115
import pinject
116
117
class DatabaseConnection(object):
118
def __init__(self):
119
self.url = "postgresql://localhost"
120
121
class CacheService(object):
122
def __init__(self):
123
self.data = {}
124
125
class BusinessService(object):
126
@pinject.inject(['database_connection']) # Only inject database_connection
127
@pinject.copy_args_to_internal_fields # Copy all args to internal fields
128
def __init__(self, database_connection, cache_service, api_key):
129
# database_connection injected and copied to _database_connection
130
# cache_service passed directly and copied to _cache_service
131
# api_key passed directly and copied to _api_key
132
self.initialized = True
133
134
def get_data(self):
135
return {
136
'db_url': self._database_connection.url,
137
'cache_size': len(self._cache_service.data),
138
'api_key_length': len(self._api_key)
139
}
140
141
cache = CacheService()
142
cache.data['key1'] = 'value1'
143
144
obj_graph = pinject.new_object_graph()
145
service = obj_graph.provide(
146
BusinessService,
147
cache_service=cache,
148
api_key="secret123"
149
)
150
151
data = service.get_data()
152
print(data['db_url']) # "postgresql://localhost"
153
print(data['cache_size']) # 1
154
print(data['api_key_length']) # 9
155
```
156
157
### Error Handling
158
159
```python
160
import pinject
161
162
class InvalidDecoratorUsage(object):
163
# ERROR: copy_args decorators only work on __init__ methods
164
@pinject.copy_args_to_public_fields
165
def setup(self, config):
166
pass
167
168
class InvalidArgsUsage(object):
169
# ERROR: copy_args decorators don't work with *args
170
@pinject.copy_args_to_internal_fields
171
def __init__(self, service, *additional_args):
172
pass
173
174
# These will raise errors:
175
# - pinject.DecoratorAppliedToNonInitError for the first case
176
# - pinject.PargsDisallowedWhenCopyingArgsError for the second case
177
```
178
179
### Comparison: Manual vs Automatic Field Copying
180
181
```python
182
import pinject
183
184
# Manual approach (verbose)
185
class ManualService(object):
186
def __init__(self, database_service, cache_service, config):
187
self._database_service = database_service
188
self._cache_service = cache_service
189
self._config = config
190
# Additional initialization logic...
191
192
# Automatic approach (concise)
193
class AutoService(object):
194
@pinject.copy_args_to_internal_fields
195
def __init__(self, database_service, cache_service, config):
196
# Fields automatically created: _database_service, _cache_service, _config
197
# Focus on additional initialization logic only...
198
pass
199
200
# Both approaches result in identical field structure
201
obj_graph = pinject.new_object_graph()
202
manual = obj_graph.provide(ManualService)
203
auto = obj_graph.provide(AutoService)
204
205
print(hasattr(manual, '_database_service')) # True
206
print(hasattr(auto, '_database_service')) # True
207
```