Skip to content

toolbox

Classes:

Name Description
Done

An Empty Output parameter that can be used to signal that a tool has completed in Model Builder

Double

Simple Double/Float parameter with default and filter passthroughs

FeatureLayer

Simple Feature Layer parameter with filter and default passthroughs

FilePath

Simple filepath input with default and filter passthroughs

Folder

Simple Feature Layer parameter with filter and default passthroughs

Integer

Simple Integer number parameter with default and filter passthroughs

Parameter
Parameters

Wrap a list of parameters and override the index to allow indexing by name

String

Simple string input parameter with filter options and default passthrough

StringList

Simple string list with default and filter passthroughs

Toggle

Simple toggle button with a name and default state

Tool
ToolABC
ToolboxABC

Functions:

Name Description
safe_load

Safely load in tools to a toolbox placing all failed imports in a Broken Tools category

toolify

Convert a typed function into a tool for the specified Toolbox class

Attributes:

Name Type Description
ParameterTypeMap

ParameterTypeMap = dict[str, tuple[ParameterDatatype | list[ParameterDatatype] | Parameter, Callable[[Parameter], Any]]] module-attribute

Done

Bases: Parameter

An Empty Output parameter that can be used to signal that a tool has completed in Model Builder

Methods:

Name Description
__init__
Source code in src/arcpie/toolbox.py
112
113
114
115
116
117
118
119
120
121
class Done(Parameter):
    """An Empty Output parameter that can be used to signal that a tool has completed in Model Builder"""
    def __init__(self) -> None:
        self.__class__.__name__ =  __name__ = 'Parameter'
        super().__init__(
            displayName='Done',
            name='done',
            direction='Output',
            parameterType='Derived',
        )

__init__()

Source code in src/arcpie/toolbox.py
114
115
116
117
118
119
120
121
def __init__(self) -> None:
    self.__class__.__name__ =  __name__ = 'Parameter'
    super().__init__(
        displayName='Done',
        name='done',
        direction='Output',
        parameterType='Derived',
    )

Double

Bases: Parameter

Simple Double/Float parameter with default and filter passthroughs

Methods:

Name Description
__init__

Attributes:

Name Type Description
__name__
value
Source code in src/arcpie/toolbox.py
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
class Double(Parameter):
    """Simple Double/Float parameter with default and filter passthroughs"""
    __name__ = 'Parameter'
    def __init__(self, displayName: str, 
                 options: list[float]|None=None, 
                 default: float|None=None,
                 required: bool=True,
                 name: str|None=None,
                 category: str|None=None) -> None:

        self.__class__.__name__ =  __name__ = 'Parameter'
        super().__init__(
            displayName=displayName,
            # Snake Case the name
            name=name or displayName.lower().replace(' ', '_'),
            parameterType='Required' if required else 'Optional',
            datatype='GPDouble',
            direction='Input',
            category=category,
        )
        if self.filter and options:
            self.filter.list = options
        if default is not None:
            self.value = default

__name__ = 'Parameter' class-attribute instance-attribute

value = default instance-attribute

__init__(displayName, options=None, default=None, required=True, name=None, category=None)

Source code in src/arcpie/toolbox.py
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
def __init__(self, displayName: str, 
             options: list[float]|None=None, 
             default: float|None=None,
             required: bool=True,
             name: str|None=None,
             category: str|None=None) -> None:

    self.__class__.__name__ =  __name__ = 'Parameter'
    super().__init__(
        displayName=displayName,
        # Snake Case the name
        name=name or displayName.lower().replace(' ', '_'),
        parameterType='Required' if required else 'Optional',
        datatype='GPDouble',
        direction='Input',
        category=category,
    )
    if self.filter and options:
        self.filter.list = options
    if default is not None:
        self.value = default

FeatureLayer

Bases: Parameter

Simple Feature Layer parameter with filter and default passthroughs

Methods:

Name Description
__init__

Attributes:

Name Type Description
__name__
controlCLSID
value
Source code in src/arcpie/toolbox.py
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
class FeatureLayer(Parameter):
    """Simple Feature Layer parameter with filter and default passthroughs"""
    __name__ = 'Parameter'
    def __init__(self, displayName: str, 
                 options: list[str]|None=None,
                 default: str|None=None,
                 required: bool=True,
                 name: str|None=None,
                 allow_create: bool=False,
                 category: str|None=None) -> None:

        self.__class__.__name__ =  __name__ = 'Parameter'
        super().__init__(
            displayName=displayName,
            # Snake Case the name
            name=name or displayName.lower().replace(' ', '_'),
            parameterType='Required' if required else 'Optional',
            datatype='GPFeatureLayer',
            direction='Input',
            category=category,
        )
        if self.filter and options:
            self.filter.list = options
        if default is not None:
            self.value = default
        if allow_create:
            self.controlCLSID = '{60061247-BCA8-473E-A7AF-A2026DDE1C2D}'

__name__ = 'Parameter' class-attribute instance-attribute

controlCLSID = '{60061247-BCA8-473E-A7AF-A2026DDE1C2D}' instance-attribute

value = default instance-attribute

__init__(displayName, options=None, default=None, required=True, name=None, allow_create=False, category=None)

Source code in src/arcpie/toolbox.py
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
def __init__(self, displayName: str, 
             options: list[str]|None=None,
             default: str|None=None,
             required: bool=True,
             name: str|None=None,
             allow_create: bool=False,
             category: str|None=None) -> None:

    self.__class__.__name__ =  __name__ = 'Parameter'
    super().__init__(
        displayName=displayName,
        # Snake Case the name
        name=name or displayName.lower().replace(' ', '_'),
        parameterType='Required' if required else 'Optional',
        datatype='GPFeatureLayer',
        direction='Input',
        category=category,
    )
    if self.filter and options:
        self.filter.list = options
    if default is not None:
        self.value = default
    if allow_create:
        self.controlCLSID = '{60061247-BCA8-473E-A7AF-A2026DDE1C2D}'

FilePath

Bases: Parameter

Simple filepath input with default and filter passthroughs

Methods:

Name Description
__init__

Attributes:

Name Type Description
value
Source code in src/arcpie/toolbox.py
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
class FilePath(Parameter):
    """Simple filepath input with default and filter passthroughs"""
    def __init__(self, displayName: str, 
                 options: list[str]|None=None, 
                 default: str|None=None,
                 required: bool=True,
                 name: str|None=None,
                 category: str|None=None) -> None:

        self.__class__.__name__ =  __name__ = 'Parameter'
        super().__init__(
            displayName=displayName,
            # Snake Case the name
            name=name or displayName.lower().replace(' ', '_'),
            parameterType='Required' if required else 'Optional',
            datatype='DEFile',
            direction='Input',
            category=category,
        )
        if self.filter and options:
            self.filter.list = options
        if default is not None:
            self.value = default

value = default instance-attribute

__init__(displayName, options=None, default=None, required=True, name=None, category=None)

Source code in src/arcpie/toolbox.py
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
def __init__(self, displayName: str, 
             options: list[str]|None=None, 
             default: str|None=None,
             required: bool=True,
             name: str|None=None,
             category: str|None=None) -> None:

    self.__class__.__name__ =  __name__ = 'Parameter'
    super().__init__(
        displayName=displayName,
        # Snake Case the name
        name=name or displayName.lower().replace(' ', '_'),
        parameterType='Required' if required else 'Optional',
        datatype='DEFile',
        direction='Input',
        category=category,
    )
    if self.filter and options:
        self.filter.list = options
    if default is not None:
        self.value = default

Folder

Bases: Parameter

Simple Feature Layer parameter with filter and default passthroughs

Methods:

Name Description
__init__

Attributes:

Name Type Description
__name__
value
Source code in src/arcpie/toolbox.py
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
class Folder(Parameter):
    """Simple Feature Layer parameter with filter and default passthroughs"""
    __name__ = 'Parameter'
    def __init__(self, displayName: str, 
                 options: list[str]|None=None,
                 default: str|None=None,
                 required: bool=True,
                 name: str|None=None,
                 category: str|None=None) -> None:

        self.__class__.__name__ =  __name__ = 'Parameter'
        super().__init__(
            displayName=displayName,
            # Snake Case the name
            name=name or displayName.lower().replace(' ', '_'),
            parameterType='Required' if required else 'Optional',
            datatype='DEFolder',
            direction='Input',
            category=category,
        )
        if self.filter and options:
            self.filter.list = options
        if default is not None:
            self.value = default

__name__ = 'Parameter' class-attribute instance-attribute

value = default instance-attribute

__init__(displayName, options=None, default=None, required=True, name=None, category=None)

Source code in src/arcpie/toolbox.py
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
def __init__(self, displayName: str, 
             options: list[str]|None=None,
             default: str|None=None,
             required: bool=True,
             name: str|None=None,
             category: str|None=None) -> None:

    self.__class__.__name__ =  __name__ = 'Parameter'
    super().__init__(
        displayName=displayName,
        # Snake Case the name
        name=name or displayName.lower().replace(' ', '_'),
        parameterType='Required' if required else 'Optional',
        datatype='DEFolder',
        direction='Input',
        category=category,
    )
    if self.filter and options:
        self.filter.list = options
    if default is not None:
        self.value = default

Integer

Bases: Parameter

Simple Integer number parameter with default and filter passthroughs

Methods:

Name Description
__init__

Attributes:

Name Type Description
__name__
value
Source code in src/arcpie/toolbox.py
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
class Integer(Parameter):
    """Simple Integer number parameter with default and filter passthroughs"""
    __name__ = 'Parameter'
    def __init__(self, displayName: str, 
                 options: list[int]|range|None=None, 
                 default: int|None=None,
                 required: bool=True,
                 name: str|None=None,
                 category: str|None=None) -> None:

        self.__class__.__name__ =  __name__ = 'Parameter'
        super().__init__(
            displayName=displayName,
            # Snake Case the name
            name=name or displayName.lower().replace(' ', '_'),
            parameterType='Required' if required else 'Optional',
            datatype='GPLong',
            direction='Input',
            category=category,
        )
        if self.filter and options:
            if isinstance(options, range):
                if options.step:
                    self.filter.list = list(options)
                else:
                    self.filter.type = 'Range'
                    self.filter.list = [options.start, options.stop]
            else:
                self.filter.list = options
        if default is not None:
            self.value = default

__name__ = 'Parameter' class-attribute instance-attribute

value = default instance-attribute

__init__(displayName, options=None, default=None, required=True, name=None, category=None)

Source code in src/arcpie/toolbox.py
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
def __init__(self, displayName: str, 
             options: list[int]|range|None=None, 
             default: int|None=None,
             required: bool=True,
             name: str|None=None,
             category: str|None=None) -> None:

    self.__class__.__name__ =  __name__ = 'Parameter'
    super().__init__(
        displayName=displayName,
        # Snake Case the name
        name=name or displayName.lower().replace(' ', '_'),
        parameterType='Required' if required else 'Optional',
        datatype='GPLong',
        direction='Input',
        category=category,
    )
    if self.filter and options:
        if isinstance(options, range):
            if options.step:
                self.filter.list = list(options)
            else:
                self.filter.type = 'Range'
                self.filter.list = [options.start, options.stop]
        else:
            self.filter.list = options
    if default is not None:
        self.value = default

Parameter

Bases: Parameter

Methods:

Name Description
__init__
Source code in src/arcpie/toolbox.py
20
21
22
23
24
25
26
27
28
29
30
31
class Parameter(_Parameter):
    def __init__(self,
                 name: str | None = None, 
                 displayName: str | None = None, 
                 direction: None | Literal['Input', 'Output'] = None, 
                 datatype: str | None | ParameterDatatype = None, 
                 parameterType: None | Literal['Required', 'Optional', 'Derived'] = None, 
                 enabled: bool | None = None, 
                 category: str | None = None, 
                 symbology: str | None = None, 
                 multiValue: bool | None = None) -> None:
        super().__init__(name, displayName, direction, datatype, parameterType, enabled, category, symbology, multiValue)

__init__(name=None, displayName=None, direction=None, datatype=None, parameterType=None, enabled=None, category=None, symbology=None, multiValue=None)

Source code in src/arcpie/toolbox.py
21
22
23
24
25
26
27
28
29
30
31
def __init__(self,
             name: str | None = None, 
             displayName: str | None = None, 
             direction: None | Literal['Input', 'Output'] = None, 
             datatype: str | None | ParameterDatatype = None, 
             parameterType: None | Literal['Required', 'Optional', 'Derived'] = None, 
             enabled: bool | None = None, 
             category: str | None = None, 
             symbology: str | None = None, 
             multiValue: bool | None = None) -> None:
    super().__init__(name, displayName, direction, datatype, parameterType, enabled, category, symbology, multiValue)

Parameters

Bases: list[Parameter]

Wrap a list of parameters and override the index to allow indexing by name

Methods:

Name Description
__contains__
__getitem__
get
Source code in src/arcpie/toolbox.py
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
class Parameters(list[Parameter]):
    """Wrap a list of parameters and override the index to allow indexing by name"""
    @overload
    def __getitem__(self, key: SupportsIndex, /) -> Parameter: ...
    @overload
    def __getitem__(self, key: slice, /) -> list[Parameter]: ...
    @overload
    def __getitem__(self, key: str, /) -> Parameter: ...
    def __getitem__(self, key: SupportsIndex|slice|str, /) -> Parameter | list[Parameter]:
        if isinstance(key, str):
            _matches = [p for p in self if p.name == key]
            if not _matches:
                raise KeyError(key)
            if len(_matches) == 1:
                return _matches.pop()
            raise KeyError(f'{key} is used for multiple parameters')
        return self[key]

    @overload
    def get(self, key: SupportsIndex, default: _Default=None, /) -> Parameter | _Default: ...
    @overload
    def get(self, key: slice, default: _Default=None, /) -> list[Parameter] | _Default: ...
    @overload
    def get(self, key: str, default: _Default=None, /) -> Parameter | _Default: ...
    def get(self, key: SupportsIndex|slice|str, default: _Default=None, /) -> Parameter | list[Parameter] | _Default:
        try:
            return self[key]
        except KeyError:
            return default

    def __contains__(self, key: object) -> bool:
        match key:
            case str():
                return any(p.name == key for p in self)
            case Parameter():
                return any(p == key for p in self)
            case _:
                return False

__contains__(key)

Source code in src/arcpie/toolbox.py
101
102
103
104
105
106
107
108
def __contains__(self, key: object) -> bool:
    match key:
        case str():
            return any(p.name == key for p in self)
        case Parameter():
            return any(p == key for p in self)
        case _:
            return False

__getitem__(key)

__getitem__(key: SupportsIndex) -> Parameter
__getitem__(key: slice) -> list[Parameter]
__getitem__(key: str) -> Parameter
Source code in src/arcpie/toolbox.py
79
80
81
82
83
84
85
86
87
def __getitem__(self, key: SupportsIndex|slice|str, /) -> Parameter | list[Parameter]:
    if isinstance(key, str):
        _matches = [p for p in self if p.name == key]
        if not _matches:
            raise KeyError(key)
        if len(_matches) == 1:
            return _matches.pop()
        raise KeyError(f'{key} is used for multiple parameters')
    return self[key]

get(key, default=None)

get(
    key: SupportsIndex, default: _Default = None
) -> Parameter | _Default
get(
    key: slice, default: _Default = None
) -> list[Parameter] | _Default
get(
    key: str, default: _Default = None
) -> Parameter | _Default
Source code in src/arcpie/toolbox.py
95
96
97
98
99
def get(self, key: SupportsIndex|slice|str, default: _Default=None, /) -> Parameter | list[Parameter] | _Default:
    try:
        return self[key]
    except KeyError:
        return default

String

Bases: Parameter

Simple string input parameter with filter options and default passthrough

Methods:

Name Description
__init__

Attributes:

Name Type Description
value
Source code in src/arcpie/toolbox.py
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
class String(Parameter):
    """Simple string input parameter with filter options and default passthrough"""

    def __init__(self, displayName: str, 
                 options: list[str]|None=None, 
                 default: str|None=None,
                 required: bool=True,
                 name: str|None=None,
                 category: str|None=None) -> None:

        self.__class__.__name__ =  __name__ = 'Parameter'
        super().__init__(
            displayName=displayName,
            # Snake Case the name
            name=name or displayName.lower().replace(' ', '_'),
            parameterType='Required' if required else 'Optional',
            datatype='GPString',
            direction='Input',
            category=category,
        )
        if self.filter and options:
            self.filter.list = options
        if default is not None:
            self.value = default

value = default instance-attribute

__init__(displayName, options=None, default=None, required=True, name=None, category=None)

Source code in src/arcpie/toolbox.py
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
def __init__(self, displayName: str, 
             options: list[str]|None=None, 
             default: str|None=None,
             required: bool=True,
             name: str|None=None,
             category: str|None=None) -> None:

    self.__class__.__name__ =  __name__ = 'Parameter'
    super().__init__(
        displayName=displayName,
        # Snake Case the name
        name=name or displayName.lower().replace(' ', '_'),
        parameterType='Required' if required else 'Optional',
        datatype='GPString',
        direction='Input',
        category=category,
    )
    if self.filter and options:
        self.filter.list = options
    if default is not None:
        self.value = default

StringList

Bases: Parameter

Simple string list with default and filter passthroughs

Methods:

Name Description
__init__

Attributes:

Name Type Description
values
Source code in src/arcpie/toolbox.py
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
class StringList(Parameter):
    """Simple string list with default and filter passthroughs"""

    def __init__(self, displayName: str, 
                 options: list[str]|None=None, 
                 defaults: list[str]|None=None,
                 required: bool=True,
                 name: str|None=None,
                 category: str|None=None) -> None:

        self.__class__.__name__ =  __name__ = 'Parameter'
        super().__init__(
            displayName=displayName,
            # Snake Case the name
            name=name or displayName.lower().replace(' ', '_'),
            parameterType='Required' if required else 'Optional',
            datatype='GPString',
            direction='Input',
            category=category,
            multiValue=True,
        )
        if self.filter and options:
            self.filter.list = options
        if defaults:
            self.values = defaults

values = defaults instance-attribute

__init__(displayName, options=None, defaults=None, required=True, name=None, category=None)

Source code in src/arcpie/toolbox.py
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
def __init__(self, displayName: str, 
             options: list[str]|None=None, 
             defaults: list[str]|None=None,
             required: bool=True,
             name: str|None=None,
             category: str|None=None) -> None:

    self.__class__.__name__ =  __name__ = 'Parameter'
    super().__init__(
        displayName=displayName,
        # Snake Case the name
        name=name or displayName.lower().replace(' ', '_'),
        parameterType='Required' if required else 'Optional',
        datatype='GPString',
        direction='Input',
        category=category,
        multiValue=True,
    )
    if self.filter and options:
        self.filter.list = options
    if defaults:
        self.values = defaults

Toggle

Bases: Parameter

Simple toggle button with a name and default state

Methods:

Name Description
__init__

Attributes:

Name Type Description
value
Source code in src/arcpie/toolbox.py
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
class Toggle(Parameter):
    """Simple toggle button with a name and default state"""

    def __init__(self, displayName: str, 
                 default: bool=False, 
                 name: str|None=None,
                 category: str|None=None) -> None:

        self.__class__.__name__ =  __name__ = 'Parameter'
        super().__init__(
            displayName=displayName,
            # Snake Case the name
            name=name or displayName.lower().replace(' ', '_'),
            parameterType='Required',
            datatype='GPBoolean',
            direction='Input',
            category=category,
        )
        self.value = default

value = default instance-attribute

__init__(displayName, default=False, name=None, category=None)

Source code in src/arcpie/toolbox.py
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
def __init__(self, displayName: str, 
             default: bool=False, 
             name: str|None=None,
             category: str|None=None) -> None:

    self.__class__.__name__ =  __name__ = 'Parameter'
    super().__init__(
        displayName=displayName,
        # Snake Case the name
        name=name or displayName.lower().replace(' ', '_'),
        parameterType='Required',
        datatype='GPBoolean',
        direction='Input',
        category=category,
    )
    self.value = default

Tool

Bases: ToolABC

Attributes:

Name Type Description
active_map Map | None
project Project | None

Get the current project that the tool is running in if it exists (otherwise: None)

Source code in src/arcpie/toolbox.py
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
class Tool(ToolABC):
    _current_project: Project | None = None

    @property
    def project(self) -> Project | None:
        """Get the current project that the tool is running in if it exists (otherwise: None)"""
        if __class__._current_project is None:
            try:
                __class__._current_project = Project('CURRENT')
            except Exception:
                pass
        return __class__._current_project

    @property
    def active_map(self) -> Map | None:
        if self.project and self.project.aprx.activeMap:
            return Map(self.project.aprx.activeMap, parent=self.project)

active_map property

project property

Get the current project that the tool is running in if it exists (otherwise: None)

ToolABC

Bases: ABC

Methods:

Name Description
__init__
execute
getParameterInfo
isLicensed
postExecute
updateMessages
updateParameters

Attributes:

Name Type Description
category str | None
description str
label str
Source code in src/arcpie/toolbox.py
39
40
41
42
43
44
45
46
47
48
49
50
class ToolABC(ABC):
    def __init__(self) -> None:
        self.label: str = self.__class__.__name__
        self.description: str = self.__doc__ or 'No Descrption Provided'
        self.category: str | None = None

    def getParameterInfo(self) -> Parameters | list[Parameter]: return Parameters()
    def isLicensed(self) -> bool: return True
    def updateParameters(self, parameters: Parameters | list[Parameter]) -> None: ...
    def updateMessages(self, parameters: Parameters | list[Parameter]) -> None: ...
    def execute(self, parameters: Parameters | list[Parameter], messages: Any) -> None: ...
    def postExecute(self, parameters: Parameters | list[Parameter]) -> None: ...

category = None instance-attribute

description = self.__doc__ or 'No Descrption Provided' instance-attribute

label = self.__class__.__name__ instance-attribute

__init__()

Source code in src/arcpie/toolbox.py
40
41
42
43
def __init__(self) -> None:
    self.label: str = self.__class__.__name__
    self.description: str = self.__doc__ or 'No Descrption Provided'
    self.category: str | None = None

execute(parameters, messages)

Source code in src/arcpie/toolbox.py
49
def execute(self, parameters: Parameters | list[Parameter], messages: Any) -> None: ...

getParameterInfo()

Source code in src/arcpie/toolbox.py
45
def getParameterInfo(self) -> Parameters | list[Parameter]: return Parameters()

isLicensed()

Source code in src/arcpie/toolbox.py
46
def isLicensed(self) -> bool: return True

postExecute(parameters)

Source code in src/arcpie/toolbox.py
50
def postExecute(self, parameters: Parameters | list[Parameter]) -> None: ...

updateMessages(parameters)

Source code in src/arcpie/toolbox.py
48
def updateMessages(self, parameters: Parameters | list[Parameter]) -> None: ...

updateParameters(parameters)

Source code in src/arcpie/toolbox.py
47
def updateParameters(self, parameters: Parameters | list[Parameter]) -> None: ...

ToolboxABC

Bases: ABC

Methods:

Name Description
__init__

Attributes:

Name Type Description
alias str
label str
tools list[type[ToolABC]]
Source code in src/arcpie/toolbox.py
33
34
35
36
37
class ToolboxABC(ABC):
    def __init__(self) -> None:
        self.label: str
        self.alias: str
        self.tools: list[type[ToolABC]]

alias instance-attribute

label instance-attribute

tools instance-attribute

__init__()

Source code in src/arcpie/toolbox.py
34
35
36
37
def __init__(self) -> None:
    self.label: str
    self.alias: str
    self.tools: list[type[ToolABC]]

safe_load(tools, *, scope=None, reload_module=False)

Safely load in tools to a toolbox placing all failed imports in a Broken Tools category

Parameters:

Name Type Description Default
tools dict[str, list[str]]

A mapping of tool modules to tool files or tool classes in a module

required
scope dict[str, Any]

The globals() dict for the Toolbox scope (required so loading happend in Toolbox scope)

None
reload_module bool

Reload the module after importing (default: False)

False

Returns:

Type Description
list[type[ToolABC]]

A list of tool classes

Note

To isolate bugs in a large toolbox, it is reccomended that you use file/module level importing with matching toolclass names (e.g. Tool.py -> class Tool) where the Tool mapping is: Tools = {'tools': ['Tool.py']} instead of: Tools = {'tools.Tool': ['Tool']} This allows the import to fail softly on a single tool instead of breaking imports for all other toolclasses in that Tool.py module.

Any tools with errors will be placed in a Broken Tools category in the toolbox with the full stack trace placed in its description field. The final component of the exception will be placed at the end of the tool label for easy debugging.

Example
>>> # File import
>>> Tools = {
...     # import matching class from named file in a submodule (tools)
...     # where ToolFileA is ToolFileA.py with a class ToolFileA
...     'tools': ['ToolFileA', 'ToolFileB'],
...     
...     # Import explicit ToolClass from toolfile (MyTools.py)
...     # NOT RECOMMENDED
...     'tools.MyTools': ['ToolClassA', 'ToolClassB'],
... }
>>> tools = safe_import(globals(), Tools)
Source code in src/arcpie/toolbox.py
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
def safe_load(tools: dict[str, list[str]],
              *,
              scope: dict[str, Any]|None=None,
              reload_module: bool=False) -> list[type[ToolABC]]:
    """Safely load in tools to a toolbox placing all failed imports in a `Broken Tools` category

    Args:
        tools (dict[str, list[str]]): A mapping of tool modules to tool files or tool classes in a module
        scope (dict[str, Any]): The `globals()` dict for the Toolbox scope (required so loading happend in Toolbox scope)
        reload_module (bool): Reload the module after importing (default: False)

    Returns:
        ( list[type[ToolABC]] ): A list of tool classes

    Note:
        To isolate bugs in a large toolbox, it is reccomended that you use file/module level importing
        with matching toolclass names (e.g. `Tool.py -> class Tool`) where the Tool mapping is:
            `Tools = {'tools': ['Tool.py']}`
        instead of:
            `Tools = {'tools.Tool': ['Tool']}`
        This allows the import to fail softly on a single tool instead of breaking imports for all other
        toolclasses in that `Tool.py` module.

        Any tools with errors will be placed in a `Broken Tools` category in the toolbox with the full
        stack trace placed in its description field. The final component of the exception will be placed
        at the end of the tool label for easy debugging.

    Example:
        ```python
        >>> # File import
        >>> Tools = {
        ...     # import matching class from named file in a submodule (tools)
        ...     # where ToolFileA is ToolFileA.py with a class ToolFileA
        ...     'tools': ['ToolFileA', 'ToolFileB'],
        ...     
        ...     # Import explicit ToolClass from toolfile (MyTools.py)
        ...     # NOT RECOMMENDED
        ...     'tools.MyTools': ['ToolClassA', 'ToolClassB'],
        ... }
        >>> tools = safe_import(globals(), Tools)
        ```
    """
    _tools = [
        _get_tool(module, tool_name, reload_module)
        for module in tools
        for tool_name in tools[module]
    ]
    if scope:
        scope.update({tool.__name__: tool for tool in _tools})
    return _tools

toolify(*tool_registries, name=None, params=None, debug=False, logger=None)

Convert a typed function into a tool for the specified Toolbox class

Parameters:

Name Type Description Default
*tool_registries list[type[ToolABC]]

The tool registry lists to add this tool to

()
name str

The name of the tool

None
params ParameterTypeMap

A mapping of parameter names to Parameter types and a callable constructor that converts the parameter to the expected value for the function parameter. You can also pass a fully formed arcpy.Parameter object as the first item in the tuple instead of a simple type

None
debug bool

Print the converted arguments to the ArcGIS Pro message console (default: False)

False
logger Logger | None

An optional logger to use for logging all runs of the toolified tool

None
Usage
>>> @toolify(
>>>     TOOL_REGISTRY, 
>>>     name='PDF Exporter', 
>>>     params={
>>>         'project': ('DEFile', lambda p: Project(p.valueAsText)),
>>>         'outfile': ('DEFile', lambda p: Path(p.valueAsText))
>>>     }
>>> )
>>> def export_pdf(project: Project|str='CURRENT', outfile: Path|str='out.pdf') -> None:
>>>     ...
Source code in src/arcpie/toolbox.py
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
def toolify(*tool_registries: list[type[ToolABC]], 
            name: str|None=None, 
            params: ParameterTypeMap|None=None, 
            debug: bool=False, 
            logger: Logger|None=None,
):
    """Convert a typed function into a tool for the specified Toolbox class

    Args:
        *tool_registries (list[type[ToolABC]]): The tool registry lists to add this tool to
        name (str): The name of the tool
        params (ParameterTypeMap): A mapping of parameter names to Parameter types and a callable constructor 
            that converts the parameter to the expected value for the function parameter. You can also pass
            a fully formed arcpy.Parameter object as the first item in the tuple instead of a simple type
        debug (bool): Print the converted arguments to the ArcGIS Pro message console (default: False)
        logger (Logger|None): An optional logger to use for logging all runs of the toolified tool

    Usage:
        ```python
        >>> @toolify(
        >>>     TOOL_REGISTRY, 
        >>>     name='PDF Exporter', 
        >>>     params={
        >>>         'project': ('DEFile', lambda p: Project(p.valueAsText)),
        >>>         'outfile': ('DEFile', lambda p: Path(p.valueAsText))
        >>>     }
        >>> )
        >>> def export_pdf(project: Project|str='CURRENT', outfile: Path|str='out.pdf') -> None:
        >>>     ...
        ```
    """
    def _builder(func: Callable[..., Any]):

        @wraps(func)
        def _execute(*args: Any, **kwargs: Any):
            return func(*args, **kwargs)

        # Build the tool class
        _label = func.__name__.replace('_', ' ').title()
        _description = func.__doc__ or 'No Description Provided'
        _class_name = _label.replace(' ', '')
        sig = inspect.signature(func)
        sig_params = sig.parameters

        # Handle parameter converison
        def _passthrough_execution(self: ToolABC, parameters: Parameters | list[Parameter], messages: Any) -> None:
            if debug:
                print(f'Executing toolified {func.__name__} via {self.label}')
            args, kwargs = _read_params(parameters, sig_params, params or {})
            start = time.time()
            try:
                if debug:
                    print(f"Using *{args}, **{kwargs}")
                res = _execute(*args, **kwargs)
                end = time.time()
                if logger:
                    logger.info(f'[{datetime.isoformat(datetime.now())}] PASS "{self.label}" ({end-start:0.2f} seconds) [{res}]')
            except Exception as e:
                print(f'Something went wrong!:\n\t{traceback.format_exc()}', severity='ERROR')
                end = time.time()
                if logger:
                    logger.info(f'[{datetime.isoformat(datetime.now())}] FAIL "{self.label}" ({end-start:0.2f} seconds) [{e}] ')

        def _local_build_params(self: ToolABC) -> Parameters | list[Parameter]:
            return _build_params(sig_params, params or {})

        def _local_init(self: ToolABC) -> None:
            self.label = name or _label
            self.description = _description

        _tool_class = type(
            _class_name, 
            (ToolABC, ),
            {
                '__init__': _local_init, 
                'getParameterInfo':_local_build_params, 
                'execute': _passthrough_execution
            }
        )

        for registry in tool_registries:
            if _tool_class.__name__ not in map(lambda c: c.__name__, registry):
                registry.append(_tool_class)
                globals()[_class_name] = _tool_class
        return _execute
    return _builder