Exercism.io automation with pytest and version control - python

CI newbie here. I'm currently working on the python track for Exercism.io. I'm looking for a way to automate the process of running tests from pytest, committing and pushing to github, and finally submitting to exercism if all tests pass. I've implemented a pre-commit hook to invoke tests on commit but I'm not sure how to pass the filename back to exercism for submission. Any help is greatly appreciated!

I put together a solution for anyone else looking for a similar workflow. It utilizes a python script in the root directory and handles the git commands via subprocess. It also supports autocomplete for the file directory (based on this gist). This script assumes you've already initialized the git repo and setup your remote repo as well.
#!/usr/bin/env python
import pytest
import subprocess
import readline
import glob
class tabCompleter(object):
"""
A tab completer that can complete filepaths from the filesystem.
Partially taken from:
http://stackoverflow.com/questions/5637124/tab-completion-in-pythons-raw-input
"""
def pathCompleter(self,text,state):
"""
This is the tab completer for systems paths.
Only tested on *nix systems
"""
return [x for x in glob.glob(text+'*')][state]
if __name__=="__main__":
# Take user input for commit message
commit_msg = raw_input('Enter the commit message: ')
t = tabCompleter()
readline.set_completer_delims('\t')
# Necessary for MacOS, Linux uses tab: complete syntax
if 'libedit' in readline.__doc__:
readline.parse_and_bind("bind ^I rl_complete")
else:
readline.parse_and_bind("tab: complete")
#Use the new pathCompleter
readline.set_completer(t.pathCompleter)
#Take user input for exercise file for submission
ans = raw_input("Select the file to submit to Exercism: ")
if pytest.main([]) == 0:
"""
If all tests pass:
add files to index,
commit locally,
submit to exercism.io,
then finally push to remote repo.
"""
subprocess.call(["git","add","."])
subprocess.call(["git","commit","-m", commit_msg])
subprocess.call(["exercism","submit",ans])
subprocess.call(["git","push","origin","master"])

Related

How to print to terminal like git log?

I'm writing a simple CLI application using python.
I have a list of records that I want to print in the terminal and I would like to output them just like git log does. So as a partial list you can load more using the down arrow, from which you quit by typing "q" (basically like the output of less, but without opening and closing a file).
How does git log do that?
You can pipe directly to a pager like this answer should work.
Alternatively, you can use a temporary file:
import os
import tempfile
import subprocess
# File contents for testing, replace with whatever
file = '\n'.join(f"{i} abc 123"*15 for i in range(400))
# Save the file to the disk
with tempfile.NamedTemporaryFile('w+', delete=False) as f:
f.write(file)
# Run `less` on the saved file
subprocess.check_call(["less", f.name])
# Delete the temporary file now that we are done with it.
os.unlink(f.name)
Device you are looking for is called pager, there exists pipepager function inside pydoc, which is not documented in linked pydoc docs, but using interactive python console you might learn that
>>> help(pydoc.pipepager)
Help on function pipepager in module pydoc:
pipepager(text, cmd)
Page through text by feeding it to another program.
therefore it seems that you should use this as follows
import pydoc
pydoc.pipepager("your text here", "less")
with limitation that it does depends on availability of less command.
How does git log do that?
git log invokes less when the output will not fit on the terminal. You can check that by running git log (if the repo doesn't have a lot of commits you can just resize the terminal before running the command) and then checking the running processes like so ps aux | grep less

"/Library" directory permission denied on Mac - Python3

I'm trying to create a program that copies a directory in the library directory on mac (path : "/Library"). I use shutil which works very well in other directories but not in the Library directory...
I want to be able to compile my program, so I can't run it as root.
Here is my code :
import shutil
def copy(src_path, dir_path):
try:
shutil.copytree(src_path, dir_path)
print("Success!")
except:
print("Impossible to copy the folder...")
print("Failed!")
copy("/Users/marinnagy/Desktop/Test", "Library/Test")
I think it's because the library directory is protected and requires authentication to make changes.
Do I have to make an authentication request to the user ? Or do I need to use another method than shutil ?
Thanks for your help !
After a good deal of research and many attempts, I finally managed to copy a folder into my Library directory.
On macOS, the process of writing to a protected directory like the Library directory is blocked for python program. Once compiled (I use pyinstaller), it seems to be impossible for a python application to access this kind of folder, even if you give the app Full Disk Access in the System Preferences.
So I used some AppleScript to manage this specific copy/paste task :
on run {scr_path, dir_path} # Run with arguments
# Translate standard paths to their quoted form
set formated_scr_path to quoted form of scr_path
set formated_dir_path to quoted form of dir_path
# Run a simple shell script to copy the repertory in the other
do shell script "cp -R " & formated_scr_path & space & formated_dir_path ¬
with administrator privileges # Ask for administrator privileges
end run
Then, in my python program, I call the AppleScript program when I want to copy/past to a protected repertory like the Library repertory :
import subprocess
def copy(scr_path, dir_path):
# Use the osascript process to call the AppleScript
# Give the paths in arguments
process = subprocess.call(['osascript', "path/to/applescript",
scr_path, dir_path])
return process
copy("path/to/folder 1", "path/to/folder 2")
This method worked for me on protected repertories. The AppleScript run in the background and an authentication window pop in, asking the user to identify himself as an admin :
result screenshot

Git commit Vim in personal project

I am making a small python script that should prompt the user for input. I like how git commit prompts the user with a vim prompt, and then uses this prompt to get the commit message.
Is it possible to implement this behavior in python?
I cannot use input(or stdin in general)
Very simple: put initial text into a temporary file, start an editor (determined by the well-known environment variables with fallback to vi), wait the editor to finish and get the content of the temp file.
See an example at https://chase-seibert.github.io/blog/2012/10/31/python-fork-exec-vim-raw-input.html
import tempfile
import subprocess
import os
def raw_input_editor(default=None, editor=None):
''' like the built-in raw_input(), except that it uses a visual
text editor for ease of editing. Unline raw_input() it can also
take a default value. '''
with tempfile.NamedTemporaryFile(mode='r+') as tmpfile:
if default:
tmpfile.write(default)
tmpfile.flush()
subprocess.check_call([editor or get_editor(), tmpfile.name])
tmpfile.seek(0)
return tmpfile.read().strip()
def get_editor():
return (os.environ.get('VISUAL')
or os.environ.get('EDITOR')
or 'vi')

running a script when some file is created

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.

Python pyinstaller loading files after exe creation

I am running 2.7 and i am using pyinstaller. My goal is to output a exe and also have it run my other class file. I am also using https://code.google.com/p/dragonfly/ as a framework for voice recognition. I have created another file in the examples direction under dragonfly->examples->text.py . If i run https://code.google.com/p/dragonfly/source/browse/trunk/dragonfly/examples/dragonfly-main.py?spec=svn79&r=79 with my IDE i can say voice commands and it will understand the below file i have created and the other example files that are in the dragonfly examples.
from dragonfly.all import Grammar, CompoundRule, Text, Dictation
import sys
sys.path.append('action.py')
import action
# Voice command rule combining spoken form and recognition processing.
class ExampleRule(CompoundRule):
print "This works"
spec = "do something computer" # Spoken form of command.
def _process_recognition(self, node, extras): # Callback when command is spoken.
print "Voice command spoken."
class AnotherRule(CompoundRule):
spec = "Hi there" # Spoken form of command.
def _process_recognition(self, node, extras): # Callback when command is spoken.
print "Well, hello"
# Create a grammar which contains and loads the command rule.
grammar = Grammar("example grammar") # Create a grammar to contain the command rule.
grammar.add_rule(ExampleRule()) # Add the command rule to the grammar.
grammar.add_rule(AnotherRule()) # Add the command rule to the grammar.
grammar.load()
# Load the grammar.
I noticed in console that it will output
UNKNOWN: valid paths: ['C:\\Users\\user\\workspace\\dragonfly\\dragonfly-0.6.5\\dragonfly\\examples\\action.py',etc..etc...
After i have used pyinstaller the output for that line is
UNKNOWN: valid paths: []
So its not loading the examples because it cannot find them. How can i tell pyinstaller to also load the example files when it is creating an exe? And If it does load the files how can i make sure my exe knows where the files are?
The command i am running for pyinstaller
C:\Python27\pyinstaller-2.0>python pyinstaller.py -p-paths="C:\Users\user\worksp
ace\dragonfly\dragonfly-0.6.5\dragonfly\examples\test.py" "C:\Users\user\workspa
ce\dragonfly\dragonfly-0.6.5\dragonfly\examples\dragonfly-main.py"
If I understand clearly. You have your script and some examples scripts which call your script to show that it is working?
You are missing the point.
Your script supposes to be an end product.
If you want to test functionality do it in development version.
If you want to test exe file do it by another(separated) test script.
Other thing:
Scripts and modules are totally different things.
You are trying to import your script as module and use it in example script.
I suggest you to build main entry point to script (with parameters if you need) as it is meant to be done.
And make other example script which run your script.
Or make a module and build script which uses this module.
Then build this example script to exe file which uses that module and shows it works
PyInstaller can compile one script at once. Forcing it to do unusual things is not needed.

Categories