Skip to content

arcsmith.ws

arcsmith.ws

Workspace

Helpers for setting up geodatabase workspaces inside 'Tool.execute'.

  • Create a file geodatabase with automatic parent folder creation
  • Get a workspace path for intermediate outputs, switching between memory and a scratch .gdb
  • Turn a file name or column header into a name a geodatabase will accept

Functions of the ws module

  • init_gdb - create a file geodatabase, reusing or overwriting an existing one
  • temp_space - get a workspace path for intermediate outputs (in-memory or scratch GDB)
  • clean_name - convert a file name or column header into a geodatabase-legal dataset or field name

init_gdb

Creates a file geodatabase at the specified location, creating the parent folder if it does not already exist. If the geodatabase already exists, it is reused by default, or recreated when overwrite=True.

init_gdb(folder, gdb_name, overwrite=False) -> str
Parameter Type Default Description
folder str or Path required Folder in which to create the geodatabase.
gdb_name str required Name of the geodatabase. A trailing .gdb extension, if supplied, is stripped, so "glacier" and "glacier.gdb" behave identically.
overwrite bool False What to do when the target .gdb already exists. False (default) reuses the existing geodatabase and returns its path (idempotent). True deletes and recreates it empty.

Returns

str: absolute path to the geodatabase. This is the newly created .gdb unless an existing one was reused (overwrite=False).

Existing geodatabases

arcpy.env.overwriteOutput does not apply to geodatabase creation, so CreateFileGDB raises if the .gdb already exists. init_gdb handles this automatically: by default it reuses the existing workspace, and overwrite=True forces a fresh, empty one. Reuse hands back whatever schema is already on disk, so pass overwrite=True if a clean workspace matters.

Examples

# Create a geodatabase
gdb = arcsmith.ws.init_gdb(r"C:\Projects\Glacier", "glacier")
# C:\Projects\Glacier\glacier.gdb

# Create and set as the arcpy workspace in one line
arcpy.env.workspace = arcsmith.ws.init_gdb(folder, gdb_name)

# Force a fresh, empty geodatabase even if one already exists
gdb = arcsmith.ws.init_gdb(folder, "glacier", overwrite=True)

temp_space

Returns a workspace path for intermediate outputs, either the in-memory workspace or the session scratch geodatabase.

temp_space(use_memory=True) -> str
Parameter Type Default Description
use_memory bool True If True, returns 'memory'. If False, returns arcpy.env.scratchGDB for on-disk inspection.

Returns

str: 'memory' or the absolute path to the scratch geodatabase.

Development workflow

Set use_memory=False while building a tool to inspect intermediate outputs in ArcGIS Pro. Switch back to True (the default) for production runs; no other code changes needed.

memory limitations

The 'memory' workspace does not support everything an on-disk geodatabase does. Certain field types, attribute indexes, and a handful of tools cannot write to it. If a tool fails against 'memory', use use_memory=False for compatibility, not only for inspection.

Examples

# Production: intermediates go to memory (default)
ws = arcsmith.ws.temp_space()
tmp = f"{ws}/trails_temp"
# tmp = "memory/trails_temp"

# Development: intermediates written to scratch GDB for inspection
ws = arcsmith.ws.temp_space(use_memory=False)
tmp = f"{ws}/trails_temp"
# tmp = "C:\Users\...\scratch.gdb\trails_temp"

clean_name

Converts a source name into a name a geodatabase will accept. Geodatabase names are limited to letters, digits, and underscores, must begin with a letter, and cannot be one of the words a geodatabase refuses outright. File names and spreadsheet headers routinely break all three rules.

The refused words are a fixed list, unrelated to what happens to be sitting in a given workspace: the 28 words a file geodatabase reserves, which apply equally to a dataset name and a field name, plus the names a geodatabase manages itself such as objectid, which apply to fields alone. Which set applies is what kind selects.

clean_name(name, *, kind="dataset", max_len=None, case="keep", prefix="x", strip_ext=True) -> str
Parameter Type Default Description
name str required File name, column header, or value read from a field, to convert. It is read as a name and nothing more: a path separator is illegal like any other punctuation and collapses to an underscore, so "north/12" becomes "north_12" rather than "12". Pass some_path.name to name an output after the file it came from.
kind 'dataset' or 'field' 'dataset' What the name is destined to become, which sets the length limit and which words are refused. See the table below. Keyword-only.
max_len int None Overrides the length limit implied by kind; must be at least 1. Default None (160 for 'dataset', 128 for 'field'). Longer names are truncated, and an underscore left exposed at the cut is trimmed. Pass 64 for a field if targeting a geodatabase held to the older field name limit. Keyword-only.
case 'keep', 'lower', or 'upper' 'keep' Casing applied to the result, prefix included. 'keep' (default) preserves the source casing; 'lower' and 'upper' bring a set of names to one convention. Keyword-only.
prefix str "x" Text prepended when the cleaned name would otherwise start with a digit, so "yr_" yields "yr_2024_Trails". The prefix is cleansed on the same terms as the name, so it cannot introduce an illegal character, and what survives that cleansing must begin with a letter. It is consulted only when the name needs it. Keyword-only.
strip_ext bool True If True (default), remove a trailing file extension of up to six characters that begins with a letter. Pass False to keep it, turning "trails.shp" into "trails_shp". Keyword-only.

What each kind enforces

kind Max length Words refused as a name
'dataset' 160 The 28 file geodatabase reserved words (see below)
'field' 128 Those same 28, plus the names a geodatabase manages itself: objectid, oid, fid, shape, shape_length, shape_area, globalid

The reserved words, which apply to feature class, table, and field names alike:

ADD     ALTER   AND     BETWEEN  BY      COLUMN  CREATE
DELETE  DROP    EXISTS  FOR      FROM    GROUP   IN
INSERT  INTO    IS      LIKE     NOT     NULL    OR
ORDER   SELECT  SET     TABLE    UPDATE  VALUES  WHERE

Returns

str: a name a geodatabase will accept.

Legal, not unique

The result is a legal name, not a unique one. Two different sources can clean to the same name, and no check is made against what already exists in the target workspace. Add a counter or a source-specific suffix when looping over a folder of files.

What gets changed

Rule Example
Accented characters fold to their ASCII base Café to Cafe
Letters with no accented form are transliterated, not dropped Ørsted to Orsted
Characters with no ASCII equivalent are removed site山data to sitedata
Every run of illegal characters collapses to one underscore a *** b to a_b
Leading and trailing underscores are trimmed __trails__ to trails
A name starting with a digit picks up prefix 2024 Trails to x2024_Trails
A refused word picks up a trailing underscore table to table_

How refused words are handled

A refused word keeps its wording and gains a trailing underscore, so "Shape" becomes "Shape_". That is what ArcGIS itself does when a reserved name arrives from a source that allowed it. Only a whole-name match counts, so "shape_area_calc" is left alone; stripping shape out of the middle of a name would destroy its meaning.

The list is short because a file geodatabase is proprietary rather than SQL backed. Words a broader SQL list would flag, date, user, view, union, and distinct among them, are legal here and are left untouched rather than renamed on suspicion.

Extensions

A trailing extension is stripped only when the text after the dot begins with a letter and runs no more than six characters. So .shp, .csv, and .sqlite are removed, "dem_v1.2" keeps its .2 and cleans to "dem_v1_2", and a longer extension stays put: "trails.geojson" cleans to "trails_geojson".

Pass strip_ext=False for spreadsheet and CSV headers. A header is not a file name, so a column called "temp.max" would otherwise lose its .max and clean to "temp".

Examples

from pathlib import Path

# A file name, cleaned
arcsmith.ws.clean_name("Trail Data 2024.shp")
# 'Trail_Data_2024'
arcsmith.ws.clean_name("Glacier NP (final).csv")
# 'Glacier_NP_final'

# A name that is already legal comes back untouched
arcsmith.ws.clean_name("already_clean")
# 'already_clean'

# A separator is punctuation, so a value read from a field keeps all of itself
arcsmith.ws.clean_name("GGOW_PC1993/01")
# 'GGOW_PC1993_01'
arcsmith.ws.clean_name("rings_GGOW_PC1993/01")
# 'rings_GGOW_PC1993_01'

# A name cannot start with a digit, so it picks up 'prefix'
arcsmith.ws.clean_name("2024 visitor counts.xlsx")
# 'x2024_visitor_counts'
arcsmith.ws.clean_name("2024 counts", prefix="yr_")
# 'yr_2024_counts'

# Source casing is kept unless 'case' asks otherwise, and the fold covers
# the prefix
arcsmith.ws.clean_name("Trail Miles", kind="field")
# 'Trail_Miles'
arcsmith.ws.clean_name("Trail Miles", kind="field", case="lower")
# 'trail_miles'
arcsmith.ws.clean_name("Trail Miles", kind="field", case="upper")
# 'TRAIL_MILES'
arcsmith.ws.clean_name("2024 counts", prefix="yr_", case="upper")
# 'YR_2024_COUNTS'

# Over the limit is truncated, and an underscore left at the cut is trimmed
arcsmith.ws.clean_name("Annual Visitor Summary", max_len=14)
# 'Annual_Visitor'
arcsmith.ws.clean_name("Annual Visitor Summary", max_len=7)
# 'Annual'

# Extensions come off by default
arcsmith.ws.clean_name("trails.shp")
# 'trails'
arcsmith.ws.clean_name("trails.shp", strip_ext=False)
# 'trails_shp'
arcsmith.ws.clean_name("dem_v1.2")
# 'dem_v1_2'          a version fragment is not an extension
arcsmith.ws.clean_name("trails.geojson")
# 'trails_geojson'    seven characters is past the ceiling

# Accents fold, and letters with no accented form are transliterated
arcsmith.ws.clean_name("Café Sites")
# 'Cafe_Sites'
arcsmith.ws.clean_name("Ørsted Wind Farm")
# 'Orsted_Wind_Farm'

# A refused word picks up an underscore; a word the geodatabase allows does not
arcsmith.ws.clean_name("table")
# 'table_'
arcsmith.ws.clean_name("date")
# 'date'

# System field names are legal as a feature class, refused as a field
arcsmith.ws.clean_name("objectid")
# 'objectid'
arcsmith.ws.clean_name("objectid", kind="field")
# 'objectid_'
arcsmith.ws.clean_name("Shape", kind="field")
# 'Shape_'

In practice:

# Name an output from the file it came from. The file name is what is passed,
# since the directory is not part of the name.
src = Path(r"C:\data\raw\Trail Data 2024.shp")
out = f"{gdb}/{arcsmith.ws.clean_name(src.name)}"
# '.../glacier.gdb/Trail_Data_2024'

# Import a folder of shapefiles, each under a legal name
for shp in Path(r"C:\data\raw").iterdir():
    if shp.suffix.lower() == ".shp":
        arcpy.conversion.ExportFeatures(str(shp), f"{gdb}/{arcsmith.ws.clean_name(shp.name)}")

# Field names from a CSV header row. A header is not a file name, so
# strip_ext=False keeps whatever follows a dot.
headers = ["Trail Name", "Shape", "2024 Visits", "Temp.Max"]
[arcsmith.ws.clean_name(h, kind="field", strip_ext=False) for h in headers]
# ['Trail_Name', 'Shape_', 'x2024_Visits', 'Temp_Max']