To the extent you can define your project needs clearly, you can do variable substitution inside a new method to create well -formed paths.
class PathString (str):
def __new__(cls, *args, **kwargs):
pathified = os.path.normpath(os.path.expandvars)
return pathified
test = PathString('${TMP}/${USERNAME}/my/folder')
# C:\\Users\\Steve\\AppData\\Local\\Temp\\Steve\\my\\folder
You can impose your rules in the new method (i usually enforce always-right-slashes, for example). If you have too many variables to manage with environment vars, use a string template:
class TemplatePathString(str):
def __new__(k, *args, **kwargs):
pathified = os.path.normpath(os.path.expandvars(args[0]))
return Template(pathified).substitute(kwargs)
test2 = TString("c:\\ul\\${fred}\\folder", fred = 'barney')
# 'c:\\ul\\barney\\folder'
You could just enforce a given structure by providing the templat inside of new:
class ServerPath(str):
def __new__ (k, *args, **kwargs):
server_path = "\\\\${server}\\${scene}\\${shot}\\" + args[0]
pathified = os.path.normpath(os.path.expandvars(server_path))
return Template(pathified).substitute(kwargs)
ServerPath("myFile.xyz", server = 'network', scene = '01', shot = '05')
# \\\
etwork\\01\\05\\myFile.xyz
In this particular case you could use a dictionary with the server/scene/shot info to clean up the typing too:
scene_info = {}
scene_info['server'] = 'network'
scene_info['scene'] = 'example'
scene_info['shot'] = 25
ServerPath ("myfilel.xyz", **scene)
# \\\
etwork\\example\\25\\myfilel.xyz
With a little work you could move the logic out of new into an overridable function and then use inheritance to build a whole family of structured path classes. That’s what I do for my project structures.
Since these guys all derive from string, other code will just treat them as strings. You could, however, add some instance methods to them so you could validate the path or create a new directory.