It only took about two hours of fiddle faddling and there's probably more commits from trying to get it to do the thing than anything else, but I finally pushed my first package to PyPi. :) I still need to add
I suppose I should take the time to describe it a little. The tag line is "Quick and easy editting of JSON files." which is probably a bit of a misnomer but it beats editting files by hand, in my opinion. There's three ways of interacting with it:
The basic grammar of
jsonconfigparser 0.1.0 [pypi] [github]
Technically it's 0.1.1 but that's only because I didn't know about MANIFEST.in Pull requests, patches and feature requests are all more than welcome!I suppose I should take the time to describe it a little. The tag line is "Quick and easy editting of JSON files." which is probably a bit of a misnomer but it beats editting files by hand, in my opinion. There's three ways of interacting with it:
- CLI through your shell
- An interactive prompt
- Through Python itself
CLI Implementation
Getting here is as simple as typingjsonconf
. It expects two positional arguments:- The first is a path to a json file.
- The second is the action you want to take on the file. They're detailed on the github and pypi pages but I'll go over the actions and flags a little.
Actions
These are the main actions used by the command line interface- addfile:
- Uses the -o/--other flag to update the specified json file with another. Using this command will overwrite existing keys with the values in the appended file if there are shared keys.
- Ex:
jsonconf path/to/conf.json addfile -o path/to/other.json
- addfield:
- Adds a field to the specified json file at a specified end point, optionally converts the value to a non-string type
- Ex:
jsonconf path/to/conf.json addfield -p $.age -v 25 -c int
- append:
- Adds a value to a JSON collection at the specified endpoint, optionally converts the value to a non-string type, optionally affects every found path found.
- Ex:
jsonconf path/to/conf.json append -p $.packages -v jsonconfigparser
- Ex:
jsonconf path/to/conf.json append -p$.authors.[*].name -v "Fred Arminsen" -m
- Ex:
jsonconf path/to/conf.json append -p $.ids -v 113 -c int
- delete:
- Deletes the specified endpoint, the path is optional here but if it's not passed, it deletes the whole document
- Ex:
jsonconf path/to/conf.json delete
- Ex:
jsonconf path/to/conf.json delete $.age
- edit:
- Changes the value at the specified endpoint. Optionally convert the value to a nonstring type
- Ex:
jsonconf path/to/conf.json edit -p $.age -v 22 -c int
- shell:
- Drops you into an interactive prompt working on the specified JSON file
- Ex:
jsonconf path/to/conf.json shell
Flags
These are the flags used by the command line interface- -p/--path: The path flag, this is a JSONpath path that the script will use to walk the JSON representation.
- -o/--other: Used with the addfile command to specify a second JSON file to update the current on with.
- -v/--value: Used to denote the value or values being passed to the underlying command.
- -m/--multi: Used with the append command to specify that multiple paths should be written to, defaults to False.
- -c/--convert: A string used to denote how to convert the passed in value to it's final form. Acceptable values to pass to -c are any of
str
,bool
,int
,float
,list
,dict
. You can also compose these values into a composite type as simple or complex as needed.
Interactive Prompt
Using the prompt is largely the same as the CLI. All the commands and flags are the same, however, all actions are applied to the file specified when the prompt was launched. There is also thewrite
command avaliable (which is also technically available on the CLI, too, but the CLI autosaves) to save the current status of the document.About -c/--convert and -v/--value
-c
takes a space delimited string and uses it to build a converter out of it. This converter can be as simple as just int
or as complex as dict int dict int list
which would be a dictionary with integer keys and values that are sub dictionaries with integer keys and lists for values. Of course with increasingly complex datatypes comes increasing complicated input. For that last example, the input might look like: 4=4=value
Which is confusing to look at until you break it down.-c list
will convert a space delimited string into a list of values using Python'sshlex.split
function. This is smart enough to know that values encased in subquotes are to be considered one value.-c list -v "1 2 '3 4'"
would output['1', '2', '3 4']
in Python.-c dict
will convert a space delimited string into sub strings delimited by=
and then into dictionaries.-c dict -v "key=value other=something"
creates{"key":"value", "other":"something"}
inside Python.- There is also
-c bool
which will return True for everything other thanFalse
(regardless of case),0
and0.0
-c "list int"
. If you need a dictionary with integer keys and list values, you'd specify -c "dict int list"
. But -c
is also pretty smart, if you need a dictionary with sub dictionary values, you might think, -c "dict str dict"
which would work, but you can simply use -c "dict dict"
because -c
knows that lists and dictionaries can't be used as keys, it doesn't even bother. It's also smart enough to know what -c wut
means so it returns strings and that -c int float
doesn't make sense so it just returns strings. Actually, I might alias -c
to --PHP
in the next release because when faced with something that maybe throwing an error is a good idea, it'll return strings.The basic grammar of
-c
is this:- Do I know what all the types are? If no, return string handler. Else:
- If there's just one type, return it. Else:
- If the first type isn't a list or dict, return a string handler because
-c
doesn't know what to do. Else:- If the first type is a list, build a secondary converter from the remaining parts of the original converter. Else:
- If the frist type is a dict and there's only one following type or the next type is a list or dict, build a converter that will only manipulate the values of the dictionary from the rest of the original converter. Else:
- If the first type is a dict and there's multiple remaining types and the next one is not a list or dict, build a converter that will manipulate both the key and the value from the remaining types. The next immediate type becomes the key type and the remaining types become the value type.
dict int list float
is easily constructed and the value can be passed as -v "0='1 2 3'"
since Python's sh.lex
intelligently groups matching quotes from the outside in.Bugs and explosions
Due to the way-c
is parsed it can be buggy at times, if you see something, say something. A copy of your conversion list and your value entry would be extremely helpful as I can't cover all possible permutations someone might use.-c
also currently makes no qualms about blowing up if you try to feed 3 4
through int
either.Interacting through Python
You can also import the module into your script to interact with it. The conversion sequences aren't really needed here since you can specify the exact type you'd like use instead of having to bend your mind around converting a flat value into nested dictionaries. The biggest tools here areJSONConfigParser
and the commands
module. The commands are largely the same, although in the Python API addfile
and addfield
become add_file
and add_field
respectively.Adding custom types
Add custom scalar types (most likely scalar type transformations) is as easy as importingjsonconfigparser.utils.fieldtypes
and updating the dictionary with your own mapping. For example, if you needed to have every value in a list upper case, you might add {'upper' : str.upper}
. You can also override any existing converters as well as long as you support the expected input (or don't, that's your call). Of course, you'll need to ensure that your type modifications get added to the runtime of the shell prompt.Adding custom commands
Adding custom commands is as simple as decoratoring a function withjsonconfigparser.command
. Currently, there's not an easy way to add arguments to the command line and shell prompt and the decorator scans the function argument signature for named arguments (i.e. everything that's not *args/**kwargs
) and the calling function attempts to pull them from the argparser
object (or simple storage object for the prompt). Both of these need to be extended and possibly combined so adding arguments is easy as well.
No comments:
Post a Comment