Giter Club home page Giter Club logo

Comments (15)

deanishe avatar deanishe commented on July 28, 2024

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.

deanishe avatar deanishe commented on July 28, 2024

Hey @smargh, could you describe what the functions should do?

from alfred-workflow.

fractaledmind avatar fractaledmind commented on July 28, 2024

Woops, sorry.

  • read_path(path)
    • read file at path, return None if path doesn't exist
  • write_path(data, path)
    • write data to file at path
  • append_path(data, path)
    • append data to path
  • read_json(path)
    • read JSON data at path, return as Python data types
  • write_json(data, path)
    • write data to path as JSON serialized text
  • run_filter(trigger, arg)
    • run Alfred external command named trigger with arg
  • 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.
  • strip(obj)
    • attempt to strip string, but return obj if not strippable.

from alfred-workflow.

deanishe avatar deanishe commented on July 28, 2024

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.

fractaledmind avatar fractaledmind commented on July 28, 2024

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.

deanishe avatar deanishe commented on July 28, 2024

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.

fractaledmind avatar fractaledmind commented on July 28, 2024

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.

deanishe avatar deanishe commented on July 28, 2024

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.

fractaledmind avatar fractaledmind commented on July 28, 2024

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.

fractaledmind avatar fractaledmind commented on July 28, 2024

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.

fractaledmind avatar fractaledmind commented on July 28, 2024

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.

deanishe avatar deanishe commented on July 28, 2024

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 as False 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.

fractaledmind avatar fractaledmind commented on July 28, 2024
  • can change subprocess to pipes.
  • 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 the Workflow() class is worth thinking about.

from alfred-workflow.

deanishe avatar deanishe commented on July 28, 2024

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.

deanishe avatar deanishe commented on July 28, 2024

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)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo 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.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.