Parse a cron entry in Python - python

All. I am trying to find a python module that I can use to parse a cron entry and get the next time it will run. With perl I use the Schedule::Cron::Events module but I would like to convert to python. Thanks in advance.

The documentation for python-crontab is in docstrings in the source code, as is usual for python. You can also explore the documentation via the python interpreter with the built-in help() function. The full source for python-crontab is less than 500 lines anyway and is very readable.
Example from the source code:
from crontab import CronTab
tab = CronTab()
cron = tab.new(command='/usr/bin/echo')
cron.minute().during(5,50).every(5)
cron.hour().every(4)
cron2 = tab.new(command='/foo/bar',comment='SomeID')
cron2.every_reboot()
list = tab.find('bar')
cron3 = list[0]
cron3.clear()
cron3.minute().every(1)
print unicode(tab.render())
for cron4 in tab.find('echo'):
print cron4
for cron5 in tab:
print cron5
tab.remove_all('echo')
t.write()

I could be wrong but doesn't python crontab offer ways to read and write to crontab but nothing regarding parsing the crontab to determine the time until the next time a job will be run?

Take a look at python-crontab.

Related

Using Jenkins variables/parameters in Python Script with os.path.join

I'm trying to learn how to use variables from Jenkins in Python scripts. I've already learned that I need to call the variables, but I'm not sure how to implement them in the case of using os.path.join().
I'm not a developer; I'm a technical writer. This code was written by somebody else. I'm just trying to adapt the Jenkins scripts so they are parameterized so we don't have to modify the Python scripts for every release.
I'm using inline Jenkins python scripts inside a Jenkins job. The Jenkins string parameters are "BranchID" and "BranchIDShort". I've looked through many questions that talk about how you have to establish the variables in the Python script, but with the case of os.path.join(),I'm not sure what to do.
Here is the original code. I added the part where we establish the variables from the Jenkins parameters, but I don't know how to use them in the os.path.join() function.
# Delete previous builds.
import os
import shutil
BranchID = os.getenv("BranchID")
BranchIDshort = os.getenv("BranchIDshort")
print "Delete any output from a previous build."
if os.path.exists(os.path.join("C:\\Doc192CS", "Output")):
shutil.rmtree(os.path.join("C:\\Doc192CS", "Output"))
I expect output like: c:\Doc192CS\Output
I am afraid that if I do the following code:
if os.path.exists(os.path.join("C:\\Doc",BranchIDshort,"CS", "Output")):
shutil.rmtree(os.path.join("C:\\Doc",BranchIDshort,"CS", "Output"))
I'll get: c:\Doc\192\CS\Output.
Is there a way to use the BranchIDshort variable in this context to get the output c:\Doc192CS\Output?
User #Adonis gave the correct solution as a comment. Here is what he said:
Indeed you're right. What you would want to do is rather:
os.path.exists(os.path.join("C:\\","Doc{}CS".format(BranchIDshort),"Output"))
(in short use a format string for the 2nd argument)
So the complete corrected code is:
import os
import shutil
BranchID = os.getenv("BranchID")
BranchIDshort = os.getenv("BranchIDshort")
print "Delete any output from a previous build."
if os.path.exists(os.path.join("C:\\Doc{}CS".format(BranchIDshort), "Output")):
shutil.rmtree(os.path.join("C:\\Doc{}CS".format(BranchIDshort), "Output"))
Thank you, #Adonis!

Crontab Python Script not running

I know this question has been asked before but I still haven't been able to get it to work. My crontab file just has this:
0 5 * * * /home/harry/my_env/bin/python /home/harry/compile_stats/process_tonight.py
Here's what my process_tonight.py looks like:
import datetime
import sys
sys.path.append('/home/harry/compile_stats/')
import compile # Module in above path
print("Processing last night\n")
date = str(datetime.datetime.today().year) + "-" + str(datetime.datetime.today().month) + "-" + str(datetime.datetime.today().day-1)
compile.process(date, date)
This file works perfectly fine when I just run it regularly from the command line but doesn't work when I schedule it.
I also looked at my /var/log/syslog file and the task I'm looking to run isn't showing up there.
Any ideas?
EDIT:
The time it's set to run in my example (5 A.M) is just a random time to put in. It's not running for any time I put in there.
EDIT 2#:
As per user speedyturkey I simplified my python script to better diagnose the problem:
import datetime
#import sys
#sys.path.append('/home/harry/compile_stats/')
#import compile # Module in above path
print("Processing last night\n")
date = str(datetime.datetime.today().year) + "-" + str(datetime.datetime.today().month) + "-" + str(datetime.datetime.today().day-1)
#compile.process(date, date)
Nothing is happening so I guess the problem isn't with the import.
As per the comments, I believe the problem is in how you are calling the python script in the crontab. Run the exact command you've given crontab and fix any problems it returns.
Ok, I was able to get it to work by creating a specific cron file, putting the info and there and loading it in.
So process_tonight.cron contains this:
0 5 * * * /home/harry/my_env/bin/python /home/harry/compile_stats/process_tonight.py
And I just loaded it into crontab:
crontab process_tonight.cron
Not really sure why this works and the other way doesn't (maybe someone else has an idea).
Instead of trying to modify the path from within your python script, you could do something like:
cd /home/harry/compile_stats/ && ./process_tonight.py
which would make it easier to import compile correctly. Note that this would also require making process_tonight.py executable (chmod +x process_tonight.py) and adding a shebang pointing to your Python interpreter (I guess... #!/home/harry/my_env/bin/python).
EDIT in response to Edit #2 above:
It is actually not possible to tell if it is running from the code you have written - the print statements are not being redirected. I suggest changing the code to perform some kind of side effect that you can check. For example, import subprocess and then do (example):
subprocess.call("date > /home/harry/compile_stats/date.txt")
If the script is executed properly, it will redirect the output of date to the file specified.
I know it's a bit silly, but checking your system time/timezone might be helpfull )))
I set my job to run at 5AM and when I logged in at 8AM there was no result of my script. So I spent over 1 hour trying to figure out what's the problem before I noticed that system time is incorrect and 5AM haven't come yet.
Have you tried running it from a shell script? I was having the same issue with my python script. I ended up putting the command in a shell script and running that. It threw an error that the library wasn't imported, so I installed it with pip and the --user flag. Now cron runs the shell script no issues.

How can I add a command to the Python interactive shell?

I'm trying to save myself just a few keystrokes for a command I type fairly regularly in Python.
In my python startup script, I define a function called load which is similar to import, but adds some functionality. It takes a single string:
def load(s):
# Do some stuff
return something
In order to call this function I have to type
>>> load('something')
I would rather be able to simply type:
>>> load something
I am running Python with readline support, so I know there exists some programmability there, but I don't know if this sort of thing is possible using it.
I attempted to get around this by using the InteractivConsole and creating an instance of it in my startup file, like so:
import code, re, traceback
class LoadingInteractiveConsole(code.InteractiveConsole):
def raw_input(self, prompt = ""):
s = raw_input(prompt)
match = re.match('^load\s+(.+)', s)
if match:
module = match.group(1)
try:
load(module)
print "Loaded " + module
except ImportError:
traceback.print_exc()
return ''
else:
return s
console = LoadingInteractiveConsole()
console.interact("")
This works with the caveat that I have to hit Ctrl-D twice to exit the python interpreter: once to get out of my custom console, once to get out of the real one.
Is there a way to do this without writing a custom C program and embedding the interpreter into it?
Edit
Out of channel, I had the suggestion of appending this to the end of my startup file:
import sys
sys.exit()
It works well enough, but I'm still interested in alternative solutions.
You could try ipython - which gives a python shell which does allow many things including automatic parentheses which gives you the function call as you requested.
I think you want the cmd module.
See a tutorial here:
http://wiki.python.org/moin/CmdModule
Hate to answer my own question, but there hasn't been an answer that works for all the versions of Python I use. Aside from the solution I posted in my question edit (which is what I'm now using), here's another:
Edit .bashrc to contain the following lines:
alias python3='python3 ~/py/shellreplace.py'
alias python='python ~/py/shellreplace.py'
alias python27='python27 ~/py/shellreplace.py'
Then simply move all of the LoadingInteractiveConsole code into the file ~/py/shellreplace.py Once the script finishes executing, python will cease executing, and the improved interactive session will be seamless.

Subprocess statement works in python console but not work in Serverdensity plugin?

in the python console the following statement works perfectly fine (i guess using eval that way is not really good, but its just for testing purpose in this case and will be replaced with proper parsing)
$ python
>>> import subprocess
>>> r = subprocess.Popen(['/pathto/plugin1.rb'], stdout=subprocess.PIPE, close_fds=True).communicate()[0]
>>> data = eval(r)
>>> data
{'test': 1}
when i convert this into a Serverdensity plugin however it keeps crashing the agent.py daemon everytime it executes the plugin. i was able to narrow it down to the subprocess line but could not find out why. exception catching did not seem to work also.
here how the plugin looks like:
class plugin1:
def run(self):
r = subprocess.Popen(['/pathto/plugin1.rb'], stdout=subprocess.PIPE, close_fds=True).communicate()[0]
data = eval(r)
return data
I'm quite new to work with python and cant really figure out why this wont work. Thanks a lot for ideas :)
Do you have subprocess imported in the module? Also what error are you getting could you post the error message ?
After switching my dev box (maybe because of the different python version?) i finally was able to get some proper error output.
Then it was rather simple: I really just needed to import the missing subprocess module.
For who is interested in the solution:
http://github.com/maxigs/Serverdensity-Wrapper-Plugin/blob/master/ruby_plugin.py
Not quite production ready yet, but works already for save input

Is there any Python wrapper around cron?

I'm looking for a wrapper around cron.
I've stumbled upon PyCron but it's a Python implementation, not a wrapper.
Do you know any good cron Python wrapper ?
If not, did you test PyCron, and what can you tell about it ?
//EDIT (As an answer to comment asking for more details):
I am looking for something to set a cron job in a pythonic way such as:
>>> job = CronJob(call_back)
>>> job.schedule(datetime, repeat)
>>> job.schedule(datetime2, repeat2)
And I could edit the currents job this way:
>>> jobs = loadFromCron()
>>> jobs[0].shedule().schedule(datetime, repeat)
>>> print(jobs[0])
<CronJob object - "call_back" at 2009-11-01>
Ideally, that would write and read from "crontab" under linux and use "planified tasks" under windows.
I may used the wrong terminology, is it more accurate to talk about a cron Python API ?
python-crontab allows you to read and write user crontabs via python programs.
from crontab import CronTab
tab = CronTab()
cron = tab.new(command='/foo/bar')
cron.every_reboot()
tab.write()

Categories

Resources