I'm on a mac. I've been using Launchd's WatchPaths directive to watch a directory for file changes. My script only triggers when a file is added or deleted from the watched directory.
However, the script does not trigger when a file is modified..
Essentially, I'm trying to create a DIY Dropbox for syncing my Sites folder.
Is there a way to do this via launchd, bash or python?
I think linux has something like inotify, but I am not aware of a solution for mac.
I tried my hand at the problem, using the MacFSEvents
package (available on PyPI, too):
import os
from fsevents import Observer, Stream
def callback(file_event):
print file_event.name # the path of the modified file
def main():
observer = Observer()
observe_path = os.getcwd() # just for this example
stream = Stream(callback, observe_path, file_events=True)
observer.start()
observer.schedule(stream)
if __name__ == '__main__':
main()
This will call callback any time a file is created, modified, or deleted (you can check which event occurred using the value of file_event.mask).
Note that you will probably want to observe on a thread outside of the main thread (the above program refuses to quit, even on KeyboardInterrupt). More information on the API can be found in the MacFSEvents README. Hope this helps!
Related
I have a problem and I am not sure what is going on, I have created a 'userSetup.py' file and placed it in the script folder (C:\Users*user*\Documents\maya\2022\scripts)
This is my userSetup.py file:
def importPy():
### import modules and open a command port ###
exec("import my_module", globals())
exec("cmds.commandPort(name=':1234', sourceType = 'python')")
exec("print('FINISHED')")
def tryImport():
"""
execute importPy when Maya is idle
"""
from maya.utils import executeDeferred
executeDeferred("importPy()")
tryImport()
now I have created a file 'my_module.py' and placed it in the same script folder
this is the my_module.py file:
print("my_module loaded")
def function_print(statement: str):
print(statement)
I know this is working on startup as the console in Maya reads:
my_module loaded
FINISHED
I have a successfully sent over simple commands like:
cmds.polyCube()
it works fine even returns fine which I found odd since I am not echoing the output, so the socket server is working as intended. But if I send:
my_module.function_print("please print this")
it doesn't work, but it will work if I write that exact same line in the python command line within Maya.
This doesn't really make sense to me, I am fairly new to python so I may be missing something obvious here. But all I can think is that the socket commands somehow don't have access to the global scope.
If anyone knows what's up with this, it would be much appreciated.
Thanks,
Brendan
P.S. the names to my modules and functions have been made up for this example. If that wasn't obvious.
I use a piece of software that when you close, saves your current configuration, however, next time I open the software by clicking on a file associated with it, it tries to run that file through the same configuration, which I don't want. I have therefore created a python script to first open up the app data for that program, reset the configuration back to default, and then open the program again.
This works fine if I just try to load the program without a file, but I would like to be able to click on a file associated with that program (usually I can just double click on the file to open it in the program), then use the 'open with' function in windows to select my script, and then open the file in the program once my script has cleared out the last configuration.
Currently, if I do this, it clears the configuration and opens the program but with no file loaded.
Essentially I am asking how I can pass the file to python, and then get python to pass that file to the third party application.
I am using the 'os.startfile('link to .exe') function to open the program, but how do I get the file path into python as an argument/string by clicking on the file and opening it with my script?
path = 'path/to/file/selected' # passed to python from selecting the file in windows explorer before starting script
# execute some code
os.startfile('path')
I'm a bit of a beginner when it comes to python so any help would be appreciated!
Using python 3.6 on windows 10
You can access command line arguments passed to your script using sys.argv. As long as you want to pass these arguments to some third-party application(which is an executable binary) you should use the subprocess module as os.startfile is meant to start a file with its associated application.
import sys
import subprocess
def main():
if len(sys.argv) == 1:
path = sys.argv[0]
subprocess.run(['/path/to/my.exe', path])
else:
print('Usage myscript.py <path>')
if __name__ == "__main__":
main()
If I understand your question correctly it could be done in the following fashion, relying on the subprocess module:
subprocess.Popen(["/path/to/application", "/path/to/file/to/open"])
See documentation for more details.
Is it possible to have a script run on a file when it's created if it has a specific extension?
let's call that extension "bar"
if I create the file "foo.bar", then my script will run with that file as an input.
Every time this file is saved, it would also run on the file.
Can I do that? If yes, how? If not, why?
note: If there is some technicality of why this is impossible, but I can do very close, that works too!
If you are using linux use pyinotify described on the website as follows: Pyinotify: monitor filesystem events with Python under Linux.
If you also want it to work using Mac OS X and Windows, you can have a look at this answer or this library.
You could do what Werkzeug does (this code copied directly from the link):
def _reloader_stat_loop(extra_files=None, interval=1):
"""When this function is run from the main thread, it will force other
threads to exit when any modules currently loaded change.
Copyright notice. This function is based on the autoreload.py from
the CherryPy trac which originated from WSGIKit which is now dead.
:param extra_files: a list of additional files it should watch.
"""
from itertools import chain
mtimes = {}
while 1:
for filename in chain(_iter_module_files(), extra_files or ()):
try:
mtime = os.stat(filename).st_mtime
except OSError:
continue
old_time = mtimes.get(filename)
if old_time is None:
mtimes[filename] = mtime
continue
elif mtime > old_time:
_log('info', ' * Detected change in %r, reloading' % filename)
sys.exit(3)
time.sleep(interval)
You'll have to spawn off a separate thread (which is what Werkzeug does), but that should work for you if you if you don't want to add pyinotify.
My plan is whenever a script is run, to take the current time and create a folder for the log files I generate. Each step of the script makes a new log file in this folder.
The issue is, I'm trying to figure out how to use the same folder for the duration of the script. What's happening now is each time a logger is created it gets the current time then, making a new folder each time.
Here's what the module looks like:
logger.py
...
CURRENT_TIME = # get current time
LOG_FOLDER = "logs/%s/" % CURRENT_TIME
...
def get_logger(name):
# Create a log file with the given name in the LOG_FOLDER
What happens is each time I import logger.py it recalculates CURRENT_TIME. I figured the way to avoid this is to do from logger.py import * which executes the code once for all the modules, making them have the same log folder.
The main issue is the script calls other python scripts, spawning new processes and the like. When these new processes import logger, they haven't imported it yet so they will regenerate CURRENT_TIME.
So what's a good way to fix this? One solution I thought of is have the logger use a constant folder called temp and when the main script finishes rename it to the current time (or store the current time at the beginning of the script). I don't really like this solution as if there is an exception or error and the script dies, the folder will remain temp when I want it in that case to be the time of the script so I know when it failed.
The classic solution would be to use an environment variable which will be shared between your process and all children.
Demo (save as x.py, warning: will call itself over and over again):
import os
import random
from subprocess import call
def get_logger():
return os.environ.setdefault("LOG_DIR", logger_name())
def logger_name(): return "log" + str(random.random())
print "Process " + str(os.getpid()) + " uses " + get_logger()
call(["python", "x.py"])
References: os.environ docs, the concept of environment variables
A better approach would be to implement a RotatingFileHandler and execute doRollover() at application startup.
when you first start your script write the starttime into a file in the temp directory.
When the logger runs it looks in the file in the temp directory for the time value.
If your script crashed then when you start the main script again it will replace the previous value so the logs for this run will be in the correct location.
You could even have a cleanup script remove the starttime from the temp directory.
It seems that if I want to create a very basic Cocoa application with a dock icon and the like, I would have to use Xcode and the GUI builder (w/ PyObjC).
The application I am intending to write is largely concerned with algorithms and basic IO - and thus, not mostly related to Apple specific stuff.
Basically the application is supposed to run periodically (say, every 3 minutes) .. pull some information via AppleScript and write HTML files to a particular directory. I would like to add a Dock icon for this application .. mainly to showing the "status" of the process (for example, if there is an error .. the dock icon would have a red flag on it). Another advantage of the dock icon is that I can make it run on startup.
Additional bonus for defining the dock right-click menu in a simple way (eg: using Python lists of callables).
Can I achieve this without using Xcode or GUI builders but simply using Emacs and Python?
Install the latest py2app, then make a new directory -- cd to it -- in it make a HelloWorld.py file such as:
# generic Python imports
import datetime
import os
import sched
import sys
import tempfile
import threading
import time
# need PyObjC on sys.path...:
for d in sys.path:
if 'Extras' in d:
sys.path.append(d + '/PyObjC')
break
# objc-related imports
import objc
from Foundation import *
from AppKit import *
from PyObjCTools import AppHelper
# all stuff related to the repeating-action
thesched = sched.scheduler(time.time, time.sleep)
def tick(n, writer):
writer(n)
thesched.enter(20.0, 10, tick, (n+1, writer))
fd, name = tempfile.mkstemp('.txt', 'hello', '/tmp');
print 'writing %r' % name
f = os.fdopen(fd, 'w')
f.write(datetime.datetime.now().isoformat())
f.write('\n')
f.close()
def schedule(writer):
pool = NSAutoreleasePool.alloc().init()
thesched.enter(0.0, 10, tick, (1, writer))
thesched.run()
# normally you'd want pool.drain() here, but since this function never
# ends until end of program (thesched.run never returns since each tick
# schedules a new one) that pool.drain would never execute here;-).
# objc-related stuff
class TheDelegate(NSObject):
statusbar = None
state = 'idle'
def applicationDidFinishLaunching_(self, notification):
statusbar = NSStatusBar.systemStatusBar()
self.statusitem = statusbar.statusItemWithLength_(
NSVariableStatusItemLength)
self.statusitem.setHighlightMode_(1)
self.statusitem.setToolTip_('Example')
self.statusitem.setTitle_('Example')
self.menu = NSMenu.alloc().init()
menuitem = NSMenuItem.alloc().initWithTitle_action_keyEquivalent_(
'Quit', 'terminate:', '')
self.menu.addItem_(menuitem)
self.statusitem.setMenu_(self.menu)
def writer(self, s):
self.badge.setBadgeLabel_(str(s))
if __name__ == "__main__":
# prepare and set our delegate
app = NSApplication.sharedApplication()
delegate = TheDelegate.alloc().init()
app.setDelegate_(delegate)
delegate.badge = app.dockTile()
delegate.writer(0)
# on a separate thread, run the scheduler
t = threading.Thread(target=schedule, args=(delegate.writer,))
t.setDaemon(1)
t.start()
# let her rip!-)
AppHelper.runEventLoop()
Of course, in your real code, you'll be performing your own periodic actions every 3 minutes (rather than writing a temp file every 20 seconds as I'm doing here), doing your own status updates (rather than just showing a counter of the number of files written so far), etc, etc, but I hope this example shows you a viable starting point.
Then in Terminal.App cd to the directory containing this source file, py2applet --make-setup HelloWorld.py, python setup.py py2app -A -p PyObjC.
You now have in subdirectory dist a directory HelloWorld.app; open dist and drag the icon to the Dock, and you're all set (on your own machine -- distributing to other machines may not work due to the -A flag, but I had trouble building without it, probably due to mis-installed egg files laying around this machine;-). No doubt you'll want to customize your icon &c.
This doesn't do the "extra credit" thingy you asked for -- it's already a lot of code and I decided to stop here (the extra credit thingy may warrant a new question). Also, I'm not quite sure that all the incantations I'm performing here are actually necessary or useful; the docs are pretty latitant for making a pyobjc .app without Xcode, as you require, so I hacked this together from bits and pieces of example code found on the net plus a substantial amount of trial and error. Still, I hope it helps!-)
PyObjC, which is included with Mac OS X 10.5 and 10.6, is pretty close to what you're looking for.
Chuck is correct about PyObjC.
You should then read about this NSApplication method to change your icon.
-(void)setApplicationIconImage:(NSImage *)anImage;
For the dock menu, implement the following in an application delegate. You can build an NSMenu programmatically to avoid using InterfaceBuilder.
-(NSMenu *)applicationDockMenu:(NSApplication *)sender;