What is the significance of uppercase file names? - nim-lang

I'm trying to implement a "static" function in Nim by doing this:
# File: OtherObject.nim
type OtherObject* = ref object of RootObj
proc mystatic*(_: typedesc[OtherObject]) = echo "Hi"
# File: test.nim
import OtherObject
OtherObject.mystatic()
But it fails with this error:
Error: type mismatch: got <>
but expected one of:
proc mystatic(_: typedesc[OtherObject])
But it doesn't fail if I rename OtherObject.nim to otherObject.nim ... what is the significance of file names starting with a capital letter? (This is on Nim 1.4.0 on Windows).

This is most likely related to clash between module and type symbols and not upper/lower case for file.
E.g. this example:
file.nimm
type file* = object
proc mystatic*(_: typedesc[file]) = discard
test.nim
import file
file.mystatic()
# Error: type mismatch: got <>
# but expected one of:
# proc mystatic(_: typedesc[file])
# expression: file.mystatic()
mystatic(file)
# Error: type mismatch: got <void>
# but expected one of:
# proc mystatic(_: typedesc[file])
# first type mismatch at position: 1
# required type for _: type file
# but expression 'file' is of type: void
# expression: mystatic(file)
Gives the same error. If symbols cannot be unambiguously resolved from the name alone it has type void. As soon as you rename things the name clash disappears and everything works.
Ps: there is no static functions in nim in the same sense as C++ for example. There is no global state that can be only accessed from the particular object. To do something like this you can just use the global variable in module without exporting it (or export if you want).

Related

Assigning a name to an output file with a string variable

I'm writing a program to concatenate 3 csv files into a single new output csv file. As part of this I have to ask the user for the name they want to use for the output filename. My problem is that my output filename is declared in the arguments of the function and therefore, I get an error on the first line of this function because myFile is not declared (see 2 lines later for the declaration).
def concatenate(indir="C:\\Conc", outfile="C:\\Conc\\%s.csv" %myFile):
os.chdir(indir)
myFile = input("Please type the name of the file: ")
fileList=glob.glob("*.csv")
dfList=[]
colnames=["Symbol", "Name", "LastSale", "MarketCap", "ADR TSO", "IPOYear", "Sector", "Industry", "Summary Quote", " "]
for filename in fileList:
print("merging " + filename + "...")
df=pandas.read_csv(filename, skiprows=1,header=None)
dfList.append(df)
concatDf=pandas.concat(dfList, axis=0)
concatDf.columns=colnames
concatDf = concatDf.sort_values(by=["Symbol"], axis=0, ascending=True)
concatDf.to_csv(outfile, index=None)
print("Completed. Your merged file can be found at: " + outfile + "\n")
This function is called from a menu function (as below) so I was wondering if it's possible to pass it from the menu??
if choice == "1":
myFile = input("Please type the name of the file: ")
concatenate(myFile)
menu()
But neither options seem to work because I always get an error saying that myFile is not declared. I know this is basic stuff but I'm scratching my head trying to figure it out.
Your reputation suggests you're new to stack overflow and your name suggests you're new to python. So welcome to both! Here's a quite verbose answer to hopefully make things clear.
There are a few issues here:
concatenate() takes two arguments, indir and outdir, but you're only passing one argument when you're calling it: concatenate(myFile). Your arguments are what is called keyword arguments, since you've given them names (i.e. indir and outdir). When you're only passing one argument (myFile), without using the keyword, the myFile variable is passed as the first argument, which, in this case, is the indir (this doesn't happen yet in your code, as the error you're getting precedes it and stops it from being executed). However, you seem to want the myFile variable assigned to your outfile argument. You can achieve this by being explicit in your call to concatenate() like so: concatenate(outfile=myFile). Now, your indir argument remains the default you've set (i.e. "C:\\Conc"), but your outfile argument has the value of myFile. This would, however, not fix your problem (detailed in point 2). I suggest you change the outfile argument to represent an output directory (see full example below).
This is where you get an error. Your concatenate() function has no knowledge of the myFile variable you've declared in your if-statement, as the function and your if-statement are different scopes. That's why function arguments exist, to pass data from one scope (your if-statement) to another scope (your function). So you get an error in your function saying that myFile is not declared because, to the function's eyes, it doesn't exist. It can only see indir and outfile, not myFile. Also, don't do string manipulation in arguments. I'm not sure how it works, but it surely doesn't work how you expect in this case.
You're asking the user for myFile twice. Once in the if-statement and once in your concatenate() function. You only need to ask for it once, I suggest to keep it in the function only.
Small correction: You should combine directory paths and filenames in a platform-independent manner. The os module has a function for that (os.path.join(), see example below). Paths in Windows and paths in Linux look different, the os module handles those differences for us:)
Here's a final suggestion, with comments, addressing all points:
# Import your modules
import pandas
import os # <-- We're gonna need this
# Your code
# ...
# The concatenate function, with arguments having a default value, allowing to optionally specify the input directory and the output *directory* (not output file)
def concatenate(indir="C:\\Conc", outdir="C:\\Conc"):
os.chdir(indir)
myFile = input("Please type the name of the file: ") # <-- We ask for the output filename here
# Your code
fileList=glob.glob("*.csv")
dfList=[]
colnames=["Symbol", "Name", "LastSale", "MarketCap", "ADR TSO", "IPOYear", "Sector", "Industry", "Summary Quote", " "]
for filename in fileList:
print("merging " + filename + "...")
df=pandas.read_csv(filename, skiprows=1,header=None)
dfList.append(df)
concatDf=pandas.concat(dfList, axis=0)
concatDf.columns=colnames
concatDf = concatDf.sort_values(by=["Symbol"], axis=0, ascending=True)
# Saving the data
# First, we need to create the full output path, i.e. the output directory + output filename. We use os.join.path() for that
output_path = os.path.join(outdir, myFile)
# The rest of your code
concatDf.to_csv(output_path, index=None) # <-- Note output_path, not outfile
print("Completed. Your merged file can be found at: " + output_path + "\n")
# The if-statement calling the ´concatenate()´ function
if choice == "1":
# We're calling concatenate() with no arguments, since we're asking for the filename within the function.
# You could, however, ask the user for the input directory or output directory and pass that along, like this:
# input_directory_from_user = input("Please type the path to the input directory: ")
# output_directory_from_user = input("Please type the path to the output directory: ")
# concatenate(indir=input_directory_from_user, outdir=output_directory_from_user)
concatenate()
menu()

How to substitute predicate value by a variable using LXML find() with Python 3.6

I am new to Python coding. I am able to create the output XML file. I want to use a variable which holds a string value and pass it to 'predicate' of 'find()'. Is this achievable? How to make this work?
I am using LXML package with Python 3.6. Below is my code. Area of problem is commented at the end of the code.
import lxml.etree as ET
# Create root element
root = ET.Element("Base", attrib={'Name': 'My Base Node'})
# Create first child element
FirstElement = ET.SubElement(root, "FirstNode", attrib={'Name': 'My First Node', 'Comment':'Hello'})
# Create second child element
SecondElement = ET.SubElement(FirstElement, "SecondNode", attrib={'Name': 'My Second Node', 'Comment': 'World'})
# Create XML file
XML_data_as_string = ET.tostring(root, encoding='utf8')
with open("TestFile.xml", "wb") as f:
f.write(XML_data_as_string)
# Variable to substitute in second portion of predicate
NewValue = "My Second Node"
# #### AREA OF PROBLEM ###
# Question. How to pass variable 'NewValue' in the predicate?
# Gives "SyntaxError: invalid predicate"
x = root.find("./FirstNode/SecondNode[#Name={subs}]".format(subs=NewValue))
# I commented above line and reexecuted the code with this below line
# enabled. It gave "ValueError: empty namespace prefix must be passed as None,
# not the empty string"
x = root.find("./FirstNode/SecondNode[#Name=%s]", NewValue)
As Daniel Haley said - you're missing a single quotes in #Name={subs}.
The following line works for me:
x = root.find("./FirstNode/SecondNode[#Name='{subs}']".format(subs=NewValue))
Since you use Python 3.6, you can utilize f-strings:
x = root.find(f"./FirstNode/SecondNode[#Name='{NewValue}']")
The "proper" way to solve this would be to use XPath variables, which are not supported by find() (and consequently, aren't supported by xml.etree from the standard library either) but are supported by xpath().
NewValue = "AJL's Second Node" # Uh oh, that apostrophe is going to break something!!
x_list = root.xpath("./FirstNode/SecondNode[#Name=$subs]", subs=NewValue)
x = x_list[0]
This avoids any sort of issue you might otherwise run into with quoting and escaping.
The main caveat of this method is namespace support, since it doesn't use the bracket syntax of find.
x = root.find("./{foobar.xsd}FirstNode")
# Brackets are doubled to avoid conflicting with `.format()`
x = root.find("./{{foobar.xsd}}FirstNode/SecondNode[#Name='{subs}']".format(subs=NewValue))
Instead, you must specify those in a separate dict:
ns_list = {'hello':'foobar.xsd'}
x_list = root.xpath("./hello:FirstNode/SecondNode[#Name=$subs]", namespaces=ns_list , subs=NewValue)
x = x_list[0]

Python 3.6 pathlib Path change name parent directory

The new Path package from the pathlib library, which has been added from Python 3.4, seems a powerful replacement of approaches such as os.path.join(), but I've some trouble working with it.
I have a path that can be anything from
folder_foo/file.csv
to
long/path/to/folder_foo/file.csv
I read the .csv file in folder_foo with pandas, modify it and want to save it to
folder_bar/file.csv
or
long/path/to/folder_bar/file.csv
Essentially I want to rename folder_foo to folder_bar in the Path object.
EDIT: example path code
csv_path = Path("long/path/to/folder_foo/file.csv")
Attempts
1
csv_path.parents[0] = csv_path.parents[0] + "_clean")
Which leads to the error TypeError: unsupported operand type(s) for +: 'PosixPath' and 'str', which means you cannot use + to combine a PosixPath with a str as described in TypeError: unsupported operand type(s) for +: 'PosixPath' and 'str'.
2
To solve this I tried the following:
csv_path.parents[0] = Path(str(csv_path.parents[0]) + "_clean")
Which however results in the error : TypeError: '_PathParents' object does not support item assignment.
Since PosixPath is not a list, this error is understandable.
3
Maybe .parts is a better approach, but
csv_path.parts[-2] = csv_path.parts[-2][:-3] + "bar"
results in: TypeError: 'tuple' object does not support item assignment.
Question
How can I easily rename the file's parent folder?
Would rather split this up for readability:
bar_folder = csv_path.parent.parent / 'folder_bar'
csv_path2 = bar_folder / csv_path.name
Having the destination folder as a variable also enables you to create the folder using for example:
bar_folder.mkdir(exist_ok=True)
You could also write a little function to replace the part of the path you want to change. Here's a runnable example:
from pathlib import Path
path1 = Path("a/b/c.txt")
path2 = Path("b/c.txt")
def rename_dir(path, src, dst):
# convert to list so that we can change elements
parts = list(path.parts)
# replace part that matches src with dst
parts[parts.index(src)] = dst
return Path(*parts)
rename_dir(path1, 'b', 'q')
#> PosixPath('a/q/c.txt')
rename_dir(path2, 'b', 'q')
#> PosixPath('q/c.txt')
Created at 2021-03-06 10:44:00 PST by reprexlite v0.4.2
EDIT: Found a cleaner solution without str()
csv_path2 = csv_path.parents[1] / (csv_path.parts[-2][:-3] + "bar") / csv_path.parts[-1]
# result
PosixPath('long/path/to/folder_bar/file.csv')
Path.parents gets the whole path to the folder minus the file. Path.parents[1] goes 2 levels up (long/path/to/), which is still a Path object. Then we get the last folder name with csv_path.parts[-2], which is a string. We apply [:-3] to get all string characters except "foo". This means we have "folder_". Then with + "bar" we get "folder_bar", which is added to our Path object. Finally we re-add the file name to our Path object with / csv_path.parts[-1].
Hack like solution
csv_path = Path(str(csv_path.parents[0])[:-3] + 'bar/' + csv_path.parts[-1])
It seems to me a bit unintuitive, however. There should be a more clean solution?

Why is str.translate() returning an error and how can I fix it?

import os
def rename_files():
file_list = os.listdir(r"D:\360Downloads\test")
saved_path = os.getcwd()
os.chdir(r"D:\360Downloads\test")
for file_name in file_list:
os.rename(file_name, file_name.translate(None,"0123456789"))
rename_files()
the error message is TypeError: translate() takes exactly one argument (2 given). How can I format this so that translate() does not return an error?
Hope this helps!
os.rename(file_name,file_name.translate(str.maketrans('','','0123456789')))
or
os.rename(file_name,file_name.translate({ ord(i) : None for i in '0123456789' }))
Explanation:
I think you're using Python 3.x and syntax for Python 2.x. In Python 3.x translate() syntax is
str.translate(table)
which takes only one argument, not like Python 2.x in which translate() syntax is
str.translate(table[, deletechars])
which can takes more than one arguments.
We can make translation table easily using maketrans function.
In this case, In first two parameters, we're replacing nothing to nothing and in third parameter we're specifying which characters to be removed.
We can also make translation table manually using dictionary in which key contains ASCII of before and value contains ASCII of after character.If we want to remove some character it value must be None.
I.e. if we want to replace 'A' with 'a' and remove '1' in string then our dictionary looks like this
{65: 97, 49: None}

how to convert mat file into xlsx file

After executing code, I received an error:
load('firstdiff.mat')
xlswrite('test.xlsx', firstdiff)
mat file consist only numeric values (0 and 1)
Undefined function or variable 'firstdiff'
Using load without output arguments is something which often confuses programmers. I recommend to use it with an output argument:
data=load('firstdiff.mat')
This way you get a struct containing the data of your mat file. A typical next step would be using fieldnames(data) to check which variables are present or if you already know, index a variable with an expression like data.x
In this case I assume you only have one variable in your matfile.
data=load('firstdiff.mat');
fn=fieldnames(data); %get all variable names
assert(numel(fn)==1); %assert there is only one variable in your mat, otherwise raise error
firstdiff=data.(fn{1}); %get the first variable
xlswrite('test.xlsx', firstdiff); %write it

Resources