3
\$\begingroup\$

The intent of this script is to hunt down all "*/testResults/*.xml" files and touch them, so that our build system doesn't issue errors that the files are too old (it's entirely intentional that tests are only re-run when the libraries they change have been modified)

import fnmatch
import os
import time

matches = []

# Find all xml files  (1)
for root, dirnames, filenames in os.walk('.'):
  for filename in fnmatch.filter(filenames, '*.xml'):
      matches.append(os.path.join(root, filename))

# filter to only the ones in a "/testResults/" folder  (2)
tests = [k for k in matches if "\\testResults\\" in k]

t = time.time()

# touch them
for test in tests:
    os.utime(test,(t,t))

Is there a simpler way to achieve this? Specifically to perform steps (1) and (2) in a single process, rather than having to filter the matches array? An alternative solution would be to just recursively find all folders called "/testResults/" and list the files within them.

Notes:

  • This script would also find "blah/testResults/blah2/result.xml" - I'm not worried about that, as the testResults folders will only contain test result xml files (but not all xml files are test results!)

  • This runs on Windows.

\$\endgroup\$
2
  • \$\begingroup\$ I don't think recursion is a good solution for this problem at all. You're initial solution is better. \$\endgroup\$ Jan 30, 2014 at 1:22
  • \$\begingroup\$ I meant recursively in a filesystem sense (i.e. it could be /a/testResults/ or /a/b/testResults/ etc), not in a recursive function sense. \$\endgroup\$
    – benjymous
    Jan 30, 2014 at 9:13

3 Answers 3

4
\$\begingroup\$

For combining (1) and (2) you can reverse the sort by only trying if your are under a test directory

Also this is a great use for a generator / map combo to avoid extra loops

import os 
import re

is_xml = re.compile('xml', re.I)
is_test = re.compile('testResults', re.I)

def find_xml_tests(root):
    for current, dirnames, filenames in os.walk(root):
        if is_test.search(current):
            for filename in filter(lambda p: is_xml.search(p),  filenames):
                yield  os.path.normpath(os.path.join(current, filename))

def touch(filename ):
    os.utime(test,(time.time(),time.time()))

map(touch, find_xml_tests('path/to/files'))
\$\endgroup\$
3
\$\begingroup\$

Python seems like overkill here: this is surely a job for the shell? Depending on exactly which files you want to touch, then use this:

touch -- */testResults/*.xml

or this:

find . -path '*/testResults/*.xml' -exec touch -- {} \+

You indicated that you are using Windows. But even on Windows, surely a PowerShell script (using Get-ChildItem instead of find and Set-ItemProperty -Name LastWriteTime instead of touch) would be simplest?

\$\endgroup\$
2
  • \$\begingroup\$ Yes, Windows I'm afraid! \$\endgroup\$
    – benjymous
    Jan 29, 2014 at 16:06
  • 1
    \$\begingroup\$ I've updated your question accordingly. \$\endgroup\$ Jan 29, 2014 at 16:08
0
\$\begingroup\$
import os,glob,time
t = time.time()
def touchy(fpath):
    os.utime(fpath,(t,t))
    return fpath

def findResults(directory = "/"):
    results = [] 
    search_path = os.path.join(directory,"testResults/*.xml")
    for d in filter(os.path.isdir,os.listdir(directory)):   
        results.extend( map(touchy,glob.glob(search_path)) if d == "testResults" else findResults(os.path.join(directory,d))
    return results

is probably about the best you can do if you want python

\$\endgroup\$
2
  • \$\begingroup\$ Can you revise this to avoid the long line? \$\endgroup\$ Jan 29, 2014 at 18:09
  • \$\begingroup\$ there its shorter lines ... still probably not quite pep-8 \$\endgroup\$ Jan 29, 2014 at 18:12

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.