Comments (15)
Agree with the run_alfred()
, run_applescript()
and clipboard functions (if it can be made to work reliably—I've been having problems with the clipboard on Yosemite).
I'm not clear on what the other functions are supposed to do. What does to_unicode()
do that Workflow.decode()
doesn't? How is check_query()
supposed to work, or strip()
?
from alfred-workflow.
Hey @smargh, could you describe what the functions should do?
from alfred-workflow.
Woops, sorry.
read_path(path)
- read file at
path
, return None ifpath
doesn't exist
- read file at
write_path(data, path)
- write
data
to file atpath
- write
append_path(data, path)
- append
data
topath
- append
read_json(path)
- read JSON data at
path
, return as Python data types
- read JSON data at
write_json(data, path)
- write
data
topath
as JSON serialized text
- write
run_filter(trigger, arg)
- run Alfred external command named
trigger
witharg
- run Alfred external command named
applescriptify(data)
- ensure proper quoting of strings and proper formatting of lists.
to_bool(text)
- convert 0s, 1s, true as string, false as string, to Python Boolean values
check_query(query)
- you can set necessary parameters for
query
, which this will check for. My version has hardcoded a check for a certain number of characters. I have another version that checks for a.
to end the query and run the command.
- you can set necessary parameters for
strip(obj)
- attempt to
strip
string, but returnobj
if not strippable.
- attempt to
from alfred-workflow.
How is append_path()
any different to os.path.join()
?
What do the read/write path/json functions do that the standard ones don't?
I like the AppleScript and clipboard functions, but the others seem a bit simplistic, apart from check_query()
, but how do you generalise that?
I don't know the use case, so I may be way off, but there's a certain code smell to to_bool()
and strip()
. Why do you need functions that can handle arbitrary data types?
from alfred-workflow.
I have updated all of my AppleScript functions, simplifying to asformat
and asrun
. I also added AppleScript UI functions (display_dialog
, display_notification
, display_alert
, say_text
, choose_file,
choose_folder,
choose_from_list`).
You are right about the JSON functions. I had added them before your update to Alfred-Workflow with serialization options. The read_path
, write_path
, and append_path
are all utility functions to make it easier to read and write data to files. So instead of
u_data = decode(data)
with open(path, 'w') as file_obj:
file_obj.write(u_data.encode('utf-8'))
You simply write write_path(data, path)
. It's not much, but these IO functions ensure stable encoding and proper file object handling. Many of my workflows have a lot of IO (ZotQuery has multiple files it reads and writes).
I'm also hoping to find some other utility methods for common operations. I'm thinking that the decode
function and a few of the others in that section of workflow.py
could be moved to this file (like fold_to_ascii
and is_ascii
).
from alfred-workflow.
In general, I like the idea of an AppleScript library. I'm not at all a fan of using native dialogs from workflows, but that's my opinion and not objectively correct, so yeah, let's add the AS functions.
I don't like the read/write/append to file functions nor to_bool()
. If you're using Alfred-Workflow (and Python in general) properly, all the text should be Unicode anyway, so the file functions are replacing 2-liners, and possibly hiding encoding/decoding errors. Not worth it, imo.
You're also bound to get someone who sees write_path()
and tries to save a GIF or other binary data with it, then complains it blows up/borks the file. Zen of Python no. 2: Explicit is better than implicit.
My objection to to_bool()
is as with my reservation about allowing empty queries in filter()
: it's playing too fast and loose with variable values/types. 0
and 1
already evaluate to False
and True
respectively, anyway.
Under what circumstances do the strings "true"
and "false"
regularly crop up?
strip()
is a no-go, at least as I've understood the API. Strings already have a strip()
method, and anything other than if isinstance(s, basestring): s = s.strip()
is doing it wrong.
I'm all for making the library more accessible to new coders, but not if it encourages bad Python code/practices, and I'm generally against writing functions to replace trivial 2-liners.
from alfred-workflow.
Agreed on all fronts. It's actually been a while since I've read any of the code, and much longer since I wrote it. I was indeed not very Pythonic when I wrote it, and your points are spot-on.
I was using to_bool
(don't think I am currently, need to check tho) in ZotQuery when I was (improperly?) passing in command line arguments. I was trying to pass bools but they were all strings. I wanted the Python code to be playing with bools, so I wrote that to convert the strings to bools. I'm pretty sure I figured out how to pass in bools from Alfred, so the need was obviated.
Same basic situation with strip
. Needed it for something I wasn't doing right.
I've actually just added alot of text utilities to the file, as well as used classes to split text utils from applescript utils. I've also written tests. You can find this newest file here. The text utils tests are here, and the Applescript tests are here.
from alfred-workflow.
Marvellous. I always enjoy the feeling of looking at old code I wrote and thinking, "you fool! That's not how you do that!" and fixing it.
(This is why I like Python: when I look at old Ruby/bash code I've written, my first reaction is always, "what the fuck does this do again?")
It's a great feeling knowing you've got better at something.
from alfred-workflow.
For reference, here are the top level utility functions in my newest version (linked above):
Text Utilities
decode(text, encoding='utf-8', normalization='NFC')
isascii(text)
fold_to_ascii(text, on_error='ignore')
slugify(text, max_length=0, separator='_')
smart_truncate(text, max_len=0, separator=' ')
convert_camel(camel_case)
set_clipboard(data)
get_clipboard()
find_name(name)
AppleScript Utilitites
asrun(scpt_str)
asformat(item)
run_filter(trigger, bundle_id, arg)
run_alfred(query)
display_dialog(text, answer=None, hidden=False, buttons=[], ok=None, cancel=None, title=None, icon=None, wait=0)
choose_from_list(list, title=None, prompt=None, default=[], ok=None, cancel=None, multiple=False, empty=False)
choose_file(prompt=None, file_types=[], default=None, invisibles=False, multiple=False, package=False)
choose_folder(prompt=None, default=None, invisibles=False, multiple=False, package=False)
display_notification(notification, title=None, subtitle=None, sound=None)
display_alert(alert, message=None, type=None, buttons=[], ok=None, cancel=None, wait=0)
say_text(spoken, display=None, use=None, waiting=False, save=None)
from alfred-workflow.
Just added a wrapper for subprocess.call()
that ensures UTF8 encoding and capture of stdout and stderr. Now the clipboard functions use this, and the tests still pass.
def subprocess(self, cmd, stdin=None):
# Is command shell string or list of args?
shell = True
if isinstance(cmd, list):
shell = False
# Set shell lang to UTF8
os.environ['LANG'] = 'en_US.UTF-8'
# Open pipes
proc = subprocess.Popen(cmd,
shell=shell,
stdin=subprocess.PIPE,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE)
# Run command, with optional input
if stdin:
(stdout, stderr) = proc.communicate(input=stdin.encode('utf-8'))
else:
(stdout, stderr) = proc.communicate()
# Convert newline delimited str into clean list
output = filter(None, [s.strip()
for s in self.decode(stdout).split('\n')])
if len(output) == 0:
return None
elif len(output) == 1:
return output[0]
else:
return output
from alfred-workflow.
I have just added two decorators to the utils.py
file to allow for memoizing class properties (the idea is that the decorated properties require generation, but won't change).
They are store_properties
and stored_property
. The former is a class decorator and the latter is a method decorator (making it a property).
Here is an example of their usage:
@utils.store_properties
class MyClass(object):
def __init__(self):
pass
@utils.stored_property
def first_property(self):
# some code to get data
return 'this is my first property'
@utils.stored_property
def second_property(self):
import time
time.sleep(2)
return "SECOND PROPERTY"
m = MyClass()
print(m.second_property)
from alfred-workflow.
WRT your subprocess()
function:
- You can't call it
subprocess
. You're overwriting the library in the namespace. - Don't capture
STDERR
. You don't log the output, so you're potentially hiding errors. - I think the assumption that the output should be turned into a list is wrong. At least it is in a general library.
- Why do you return
None
instead of an empty string? Both evaluate asFalse
in tests but you're now returning unexpected values.
WRT your decorator, there's no need to also decorate the class.
WRT your text utilities, I think only the clipboard functions are worth including. The others either duplicate functionality that's already in Alfred-Workflow or don't really have a place in it (I don't believe converting camel case or generating URL slugs is something lots of workflows do).
from alfred-workflow.
- can change
subprocess
topipes
. - the list return value is easily changed
- the class decorator adds the
properties
attribute to the class, which represents the dictionary written to disk - the idea of the text utilities is to decouple them from the
Workflow()
class. We can drop the slug and conversion stuff, but I think decoupling the text based functions from theWorkflow()
class is worth thinking about.
from alfred-workflow.
can change subprocess to pipes.
Can't you call it something more obvious (and appropriate), like run_command
or run_process
? It doesn't have a great deal to do with pipes, either in the CLI or Python sense.
the class decorator adds the properties attribute to the class, which represents the dictionary written to disk
Sorry. I didn't realise it was a persistent object (I'm not sure where the source code to your utils.py
is).
the idea of the text utilities is to decouple them from the Workflow() class. We can drop the slug and conversion stuff, but I think decoupling the text based functions from the Workflow() class is worth thinking about.
Absolutely. That will 100% be happening in the v2 refactoring. I'm currently still unsure how best to split up the various components of workflow.py
. I definitely want all the code that's dependent on the Alfred/workflow environment tucked away in one module for the sake of testing (which is a horrible mess atm), but beyond that, I dunno.
from alfred-workflow.
I'm closing this issue for now. It's mostly a matter of the name and lack of focus.
The features are definitely interesting, but this isn't an issue I want to add to the v2.0 milestone.
I will (or someone else can) re-add the features discussed but in a more focussed fashion, i.e. not util.py
, but applescript.py
and/or ui.py
etc.
from alfred-workflow.
Related Issues (20)
- Cache Image HOT 5
- Basic auth HOT 3
- Pass parameter to subprocess HOT 5
- Tutorial options for keywords need to be updated for Alfred 4 HOT 11
- set_config raises error when the bundle id is null HOT 4
- Setting only arg on Variables adds line break HOT 1
- will it support python3? HOT 1
- python3 has no cpickle HOT 1
- cant get output HOT 11
- chr() arg not in range(256) error when trying to use Beautiful Soup 4 HOT 1
- workflow:magic not working?
- API functionality question
- AlertCautionIcon.icns does not exist on Big Sur
- ERROR: [Script Filter] JSON error
- Google SDK
- Can't get Script Filter to find the pinboard.py file from the tutorial HOT 1
- [Feature request] Possible to open bookmarks from root?
- Not working on the latest MacOS 12.3 HOT 11
- How to fetch chrome cookie?
- Issues with notify.notify in release version HOT 2
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
D3
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
-
Recommend Topics
-
javascript
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
-
web
Some thing interesting about web. New door for the world.
-
server
A server is a program made to process requests and deliver data to clients.
-
Machine learning
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from alfred-workflow.