patternpythonMinor
Local source control
Viewed 0 times
sourcelocalcontrol
Problem
I've written my own local "source control". Rather than using a commit-based system, when you're ready to release a version, you can run a command which will create a
How does it work?
Each "project" written with this has a specific file structure that looks like the below. A file structure like this is required in order for the source control to work.
There are also three commands that are used.
Concerns
There are a few things I'm concerned about here.
```
import os
import re
import shutil
def command_new_project(project_name: str, project_description: str):
"""Generate a new project.
This function generates a new project. A
project structure looks like this:
/[project name]
/source
...
/versions
...
/info
readme.txt
changelog.txt
Keyword arguments:
project_name -- The name of the project.
project_description -- A brief description of the project.
"""
os.makedirs("./{0}".format(project_name))
os.chdir("./{0}".format(project_name))
os.makedirs("./source")
os.makedirs("./versions")
os.maked
.zip copy of your source code, and it's saved to a versions folder.How does it work?
Each "project" written with this has a specific file structure that looks like the below. A file structure like this is required in order for the source control to work.
/[Project Name]
/source
...
/versions
...
/info
changelog.txt
readme.txt
There are also three commands that are used.
new, push, and changedir. Each command argument is double colon, ::, separated, and look something like the following:new::[project Name]::[project info]- Create a new project.
push::[version name]::[version info]- Push a new version to theversionsfolder as a.zipfile.
changedir::[directory]- Change to a new directory.
Concerns
There are a few things I'm concerned about here.
- Have I designed this in a clear intuitive way? The current design right now feels clunky and hard to use.
- Am I over-documenting things? While I love documentation, if this is too much, tips on documentation would be appreciated.
- Am I correctly handling errors?
```
import os
import re
import shutil
def command_new_project(project_name: str, project_description: str):
"""Generate a new project.
This function generates a new project. A
project structure looks like this:
/[project name]
/source
...
/versions
...
/info
readme.txt
changelog.txt
Keyword arguments:
project_name -- The name of the project.
project_description -- A brief description of the project.
"""
os.makedirs("./{0}".format(project_name))
os.chdir("./{0}".format(project_name))
os.makedirs("./source")
os.makedirs("./versions")
os.maked
Solution
Obviously, the error handling needs work: you are swallowing
The docstrings are too verbose for my personal taste, and get in the way of the code.
The command dispatch mechanism could be smarter. To add a new command, you have to define a function and add a condition to
The solution for all of that, I believe, introspection. I've also used variable-length argument lists for clarity and to avoid the awkward
FileNotFoundError, discarding invalid input, and generally ignoring all kinds of failures. (In my experience, adding good error handling easily doubles the programming effort required.) There is no quit or exit command, nor do you handle EOFError gracefully.:: isn't exactly the most user-friendly token separator.The docstrings are too verbose for my personal taste, and get in the way of the code.
The command dispatch mechanism could be smarter. To add a new command, you have to define a function and add a condition to
execute_user_input(). Furthermore, you need to specify the number of expected arguments, repeating yourself. The linear search for a matching command is also inelegant by Python standards.The solution for all of that, I believe, introspection. I've also used variable-length argument lists for clarity and to avoid the awkward
command = tokenized_user_input[0]; arguments = tokenized_user_input[1:] assignments.import inspect
import os
import re
import shutil
class Commands:
@staticmethod
def new(project_name, project_description):
...
@staticmethod
def push(version_number, version_description):
...
@staticmethod
def changedir(directory):
"""Change to a new directory."""
try:
os.chdir(directory)
except FileNotFoundError:
pass # TODO: error handling
def execute_user_input(command, *arguments):
"""
Execute command, if the command exists and the correct number of arguments
have been given.
"""
function = getattr(Commands, command, None)
if function is None or command.startswith('_'):
return # TODO: error handling
argspec = inspect.getargspec(function)
if ( len(arguments) == len(argspec.args) or
len(arguments) > len(argspec.args) and argspec.varargs ):
function(*arguments)
def tokenize_user_input(user_input):
"""Tokenize user input into a list."""
return re.split("\s*::\s*", user_input)
def main():
while True:
user_input = input("lsc> ")
execute_user_input(*tokenize_user_input(user_input))
if __name__ == "__main__":
main()Code Snippets
import inspect
import os
import re
import shutil
class Commands:
@staticmethod
def new(project_name, project_description):
...
@staticmethod
def push(version_number, version_description):
...
@staticmethod
def changedir(directory):
"""Change to a new directory."""
try:
os.chdir(directory)
except FileNotFoundError:
pass # TODO: error handling
def execute_user_input(command, *arguments):
"""
Execute command, if the command exists and the correct number of arguments
have been given.
"""
function = getattr(Commands, command, None)
if function is None or command.startswith('_'):
return # TODO: error handling
argspec = inspect.getargspec(function)
if ( len(arguments) == len(argspec.args) or
len(arguments) > len(argspec.args) and argspec.varargs ):
function(*arguments)
def tokenize_user_input(user_input):
"""Tokenize user input into a list."""
return re.split("\s*::\s*", user_input)
def main():
while True:
user_input = input("lsc> ")
execute_user_input(*tokenize_user_input(user_input))
if __name__ == "__main__":
main()Context
StackExchange Code Review Q#98409, answer score: 4
Revisions (0)
No revisions yet.