I'm writing a custom Builder. Is there any way from inside the Builder to get the root path of the variant_dir it's building in?
Maybe a property on env?
Or maybe from a target node? For example, if in my Builder I have:
target[0].path --> path_to/my_variant_dir/subdir/target_file.txt
Is there a way to determine which part is the variant_dir path?
'path_to/my_variant_dir'
You specify the variant dir in the call to SConscript() so you can just store it on the env and retrieve it later. Ive actually done this before for a different reason, like this:
var_dir = 'some_dir'
env['variant_dir'] = var_dir
SConscript('subdir/SConscript', variant_dir = env['variant_dir'], duplicate = 0)
...
def yourBuilderFunc(target, source, env):
# variant_dir is env['variant_dir']
Related
I am building a script that takes a path and gets the folders. Then appends those folder names as items to another item that I am calling "Root". I am using QStandardItemModel and not QFileSystemModel because I intend to replace "Root" with a name that the user will provide and I am using QTreeView as a preview to the entire structure of the folder.
Current issue:
I would like to add the entire directory and include the sub directories but have it all appended under "Root". In my attempt below I am only getting the first set of folder.
In this example, I created a folder, with many sub folders and am trying to add those folders in correct order to my "Root" item.
import os
from PyQt6.QtWidgets import QApplication, QTreeView
from PyQt6.QtGui import QStandardItemModel, QStandardItem
app = QApplication([])
# Create the model
model = QStandardItemModel()
# Set the path to the directory containing the folders you want to add to the tree view
path = r"replace\with\path\to\folder"
# Create an Item for everything to parent under
root_item = QStandardItem("Root")
model.appendRow(root_item)
#iterate through the directory provided
for index,(root, folders, files) in enumerate(os.walk(path)):
folder_root_name = (str(root))
folder_root_name = folder_root_name.split('\\')[-1]
for folder in folders:
folder_item = QStandardItem(folder)
if index == 0:
root_item.appendRow(folder_item)
else:
folder_root_name_item = QStandardItem(folder_root_name)
folder_root_name_item.appendRow(folder_item)
tree_view = QTreeView()
tree_view.setModel(model)
tree_view.show()
app.exec()
Here is a photo of what I am trying to accomplish:
Whenever you have to deal with tree/directory structures, you have to consider that you're using a 3-dimensional model, which automatically calls for a recursive behavior.
While, normally, such a structure would require a relative 3D "model" as a reference, a basic string-based dictionary can suffice with a simple directory-based model.
The assumption is based on the fact that os.walk will always walk through sub directories, even if they are empty.
The trick is to use a dictionary that has keys as full directory paths, and items as their values.
root_item = QStandardItem("Root")
parents = {path: root_item}
model.appendRow(root_item)
def getParent(path):
parent = parents.get(path)
if parent:
return parent
grandParentPath, parentName = path.rsplit(os.sep, 1)
parent = QStandardItem(parentName)
parents[path] = parent
getParent(grandParentPath).appendRow(parent)
return parent
for root, folders, files in os.walk(path):
getParent(root)
May I know how to print the directory path which is 1 level up? (eg. the groovy file is located in "abc/def/ghi/dummy.groovy" and I want to get the "abc/def" path)
here is my dummy.groovy script
File fileCon= new File("/../")
logger.debug((String.format("[%s]", fileCon))
groovy file could be loaded from plain file, from jar, from url.
i'd not recommend to use this approach - it will not work for all cases.
def url = this.getClass().getProtectionDomain().getCodeSource()?.getLocation()
println new URL(url, '..')
Here is how you get the parent directory as File:
def file = new File('abc/def/ghi/dummy.groovy')
println "Parent: ${file.getParentFile().absolutePath}"
it will give you abc/def/ghi/. You may get parent folder from the result:
println "Parent: ${file.getParentFile().getParentFile().absolutePath}"
you'll get your desired abc/def.
I didn't see any File in GroovyDocs, so I presume this is a Java Class.
So why not just use:
def file = new File('abc/def/ghi/dummy.groovy')
def filePath = file.getParent().getParent()
How to make this code works?
There is a zip file with folders and .png files in it. Folder ".\icons_by_year" is empty. I need to get every file one by one without unzipping it and copy to the root of the selected folder (so no extra folders made).
class ArrangerOutZip(Arranger):
def __init__(self):
self.base_source_folder = '\\icons.zip'
self.base_output_folder = ".\\icons_by_year"
def proceed(self):
self.create_and_copy()
def create_and_copy(self):
reg_pattern = re.compile('.+\.\w{1,4}$')
f = open(self.base_source_folder, 'rb')
zfile = zipfile.ZipFile(f)
for cont in zfile.namelist():
if reg_pattern.match(cont):
with zfile.open(cont) as file:
shutil.copyfileobj(file, self.base_output_folder)
zfile.close()
f.close()
arranger = ArrangerOutZip()
arranger.proceed()
shutil.copyfileobj uses file objects for source and destination files. To open the destination you need to construct a file path for it. pathlib is a part of the standard python library and is a nice way to handle file paths. And ZipFile.extract does some of the work of creating intermediate output directories for you (plus sets file metadata) and can be used instead of copyfileobj.
One risk of unzipping files is that they can contain absolute or relative paths outside of the target directory you intend (e.g., "../../badvirus.exe"). extract is a bit too lax about that - putting those files in the root of the target directory - so I wrote a little something to reject the whole zip if you are being messed with.
With a few tweeks to make this a testable program,
from pathlib import Path
import re
import zipfile
#import shutil
#class ArrangerOutZip(Arranger):
class ArrangerOutZip:
def __init__(self, base_source_folder, base_output_folder):
self.base_source_folder = Path(base_source_folder).resolve(strict=True)
self.base_output_folder = Path(base_output_folder).resolve()
def proceed(self):
self.create_and_copy()
def create_and_copy(self):
"""Unzip files matching pattern to base_output_folder, raising
ValueError if any resulting paths are outside of that folder.
Output folder created if it does not exist."""
reg_pattern = re.compile('.+\.\w{1,4}$')
with open(self.base_source_folder, 'rb') as f:
with zipfile.ZipFile(f) as zfile:
wanted_files = [cont for cont in zfile.namelist()
if reg_pattern.match(cont)]
rebased_files = self._rebase_paths(wanted_files,
self.base_output_folder)
for cont, rebased in zip(wanted_files, rebased_files):
print(cont, rebased, rebased.parent)
# option 1: use shutil
#rebased.parent.mkdir(parents=True, exist_ok=True)
#with zfile.open(cont) as file, open(rebased, 'wb') as outfile:
# shutil.copyfileobj(file, outfile)
# option 2: zipfile does the work for you
zfile.extract(cont, self.base_output_folder)
#staticmethod
def _rebase_paths(pathlist, target_dir):
"""Rebase relative file paths to target directory, raising
ValueError if any resulting paths are not within target_dir"""
target = Path(target_dir).resolve()
newpaths = []
for path in pathlist:
newpath = target.joinpath(path).resolve()
newpath.relative_to(target) # raises ValueError if not subpath
newpaths.append(newpath)
return newpaths
#arranger = ArrangerOutZip('\\icons.zip', '.\\icons_by_year')
import sys
try:
arranger = ArrangerOutZip(sys.argv[1], sys.argv[2])
arranger.proceed()
except IndexError:
print("usage: test.py zipfile targetdir")
I'd take a look at the zipfile libraries' getinfo() and also ZipFile.Path() for construction since the constructor class can also use paths that way if you intend to do any creation.
Specifically PathObjects. This is able to do is to construct an object with a path in it, and it appears to be based on pathlib. Assuming you don't need to create zipfiles, you can ignore this ZipFile.Path()
However, that's not exactly what I wanted to point out. Rather consider the following:
zipfile.getinfo()
There is a person who I think is getting at this exact situation here:
https://www.programcreek.com/python/example/104991/zipfile.getinfo
This person seems to be getting a path using getinfo(). It's also clear that NOT every zipfile has the info.
I wrote a groovy script in soapui to create files in certain location in my pc. How can I make it dynamic and enable the user to write the location the files are saved to by write the location in configuration file imported at test suite level.
if(context.expand('${#Project#ProduceReports}') == 'true') {
def resultDir = new File("D:\\Reports");
if(!resultDir.exists()) {
resultDir.mkdirs();
}
def resultsFile = new File(resultDir, "CSVReport.csv");
}
If you want to get the path from a testSuite property, you can do it as you do with the project property, using context.expand:
def yourPath = context.expand('${#TestSuite#pathDirectory}')
Or alternatively you can do the same with:
def yourPath = context.testCase.testSuite.getPropertyValue('pathDirectory')
Maybe this is out of scope for your question, but could be helpful. If you need you can also use UISupport to ask the user to enter the path he wants with the follow code:
def ui = com.eviware.soapui.support.UISupport;
// the prompt question, title, and default value
def path = ui.prompt("Enter the path","Title","/base/path");
log.info path
This shows:
Define project level custom property REPORT_PATH with value D:/Reports/CSVReport.csv i.e., full path including file and path separate by / slash even on windows platform.
Then use the below script to write the data.
//Define the content that goes as report file. Of course, you may change the content as need by you
def content = """Name,Result
Test1,passed
Test2,failed"""
//Read the project property where path is configured
def reportFileName = context.expand('${#Project#REPORT_PATH}')
//Create file object for reports
def reportFile = new File(reportFileName)
//Create parent directories if does not exists
if (!reportFile.parentFile.exists()) {
reportFile.parentFile.mkdirs()
}
//Write the content into file
reportFile.write(content)
I'm trying to generate a per-sourcefile macro that will hold the base file name of the source file. This is described for make here.
I tried to override the Object builder, but it didn't work...
I tried to do what is described here.
def my_object_builder(env, target, source, **kwargs):
"""A builder that calls the Object builder, with the addition of defining
a macro that holds the source file's basename
"""
if SCons.Util.is_List(source):
if len(source) > 1:
raise ValueError('cannot pass a list of sources to Object builder: %s',
[str(x) for x in source])
else:
source, = source
if 'CPPDEFINES' not in kwargs:
kwargs['CPPDEFINES'] = []
kwargs['CPPDEFINES'].append(('__MY_FILENAME',
os.path.basename(str(source))))
ret = env._Object(target=target,
source=source,
**kwargs)
return ret
Then, replacing the builders:
env['BUILDERS']['_Object'] = env['BUILDERS']['Object']
env['BUILDERS']['Object'] = my_object_builder
This didn't work. I got the following error:
AttributeError: 'function' object has no attribute 'src_suffixes'
I think it has to do with something with Environment's MethodWrapper, but I couldn't verify.
Maybe I'm going for this from the wrong angle. Maybe I should change the environment for each source file (seems like a lot of work...)
The main requirement is that the usage will be seamless. I don't want users to have to call a MyObjectBuilder class. Also, the StaticLibrary Builder should call the new Object builder.
Any help would be much appreciated.
Thanks!
BugoK.
Found it. It's a one liner...
env.Append(CCFLAGS=['-D__MY_FILENAME=\\"${SOURCE.file}\\"'])
Just adding a define which is rule based.
The quotes are just an addition to be able to printf it without strigifying the expression.