Defining a field which holds a procedure fails [Nim] - nim-lang

I am creating a type which holds a procedure as one of its fields. When I attempt to compile the file, it provides me with the error 'CommandC' is not a concrete type.
The problematic snippet is seen below.
type
CommandC = object
tocall: proc (info: CommandC, vrs: SharedTable)
arguments: seq[seq[string]]
subcommands: seq[CommandC]
CommandP = object
cat: string
tocall: proc (info: CommandC, vrs: SharedTable) # This line raises an error during compilation
arguments: seq[string]
textdata: string
I am new to Nim [transitioning from Python for larger projects] and for the life of me cannot figure out what this actually means, or how to fix it. This is likely plain old incompetence on my part.
[I am also new to using Stack Overflow and so if my question is not up to standards then that's my bad and I apologize]

I guess you have found it yourself already. But in case that it was not that obvious, you may understand that Nim is not Python, but a statically typed, compiled language. An unspecified data type like SharedTable can not exist. A table (map) is a mapping from one type to another type, so something like SharedTable[string, int] makes more sense, and the code below compiles for me:
import std/sharedtables
type
CommandC = object
tocall: proc (info: CommandC, vrs: SharedTable[string, int])
arguments: seq[seq[string]]
subcommands: seq[CommandC]
CommandP = object
cat: string
tocall: proc (info: CommandC, vrs: SharedTable[string, int]) # This line raises an error during compilation
arguments: seq[string]
textdata: string

Related

Is there correct way to use python typing?

I'm trying to figure out typing in Python and I'm having trouble with the following code:
from collections.abc import Iterable
from typing import TypeVar
T = TypeVar('T')
def convert_to_iter(var: T | Iterable[T]) -> Iterable[T]:
if not isinstance(var, Iterable):
return (var, )
return var
I am using Pylance in VScode with typeCheckingMode: "strict" and am getting the error on the last line of the code:
Return type, "Iterable[Unknown]* | Iterable[T#convert_to_list]", is partially unknown Pylance(reportUnknownVariableType)
Can someone please explain why this is incorrect?
There is a discussion in this issue about such question.
So, this behavior can be explained by type narrowing.
One of the existing ways to eliminate this error is to use cast function

Extract type hints for object attributes in Python [duplicate]

I want to get the type hints for an object's attributes. I can only get the hints for the class and not an instance of it.
I have tried using foo_instance.__class__ from here but that only shows the class variables.
So in the example how do I get the type hint of bar?
class foo:
var: int = 42
def __init__(self):
self.bar: int = 2
print(get_type_hints(foo)) # returns {'var': <class 'int'>}
I just had the same problem. The python doc isn't that clear since the example is made with what is now officially called dataclass.
Student(NamedTuple):
name: Annotated[str, 'some marker']
get_type_hints(Student) == {'name': str}
get_type_hints(Student, include_extras=False) == {'name': str}
get_type_hints(Student, include_extras=True) == {
'name': Annotated[str, 'some marker']
}
It give the impression that get_type_hints() works on class directly. Turns out get_type_hints() returns hints based on functions, not on class. That way it can be use with both if we know that. A normal class obviously not being instantiated at it's declaration, it does not have any of the variables set within the __init__() method who hasn't yet been called. It couldn't be that way either if we want the possibility to get the type hints from class-wide variables.
So you could either call it on __init__(), that is if variables are passed in arguments though (yes i seen it's not in your example but might help others since i didn't seen this anywhere in hours of search);
class foo:
var: int = 42
def __init__(self, bar: int = 2):
self.bar = int
print(get_type_hints(foo.__init__))
At last for your exact example i believe you have two choices. You could instantiate a temporary object and use del to clean it right after if your logic allows it. Or declare your variables as class ones with or without default values so you can get them with get_type_hints() and assign them later in instantiations.
Maybe this is a hack, and you have to be the creator of your instances, but there are a subset of cases in which using a data class will get you what you want;
Python 3.7+
#dataclass
class Foo:
bar: str = 2
if __name__ == '__main__':
f = Foo()
print(f.bar)
print(get_type_hints(f))
2
{'bar': <class 'str'>}
Hints only exist at the class level — by the time an instance is created the type of its attributes will be that of whatever value has been assigned to them. You can get the type of any instance attribute by using the first form of the built-in type() function — e.g. type(foo_instance.var).
This information isn't evaluated and only exists in the source code.
if you must get this information, you can use the ast module and extract the information from the source code yourself, if you have access to the source code.
You should also ask yourself if you need this information because in most cases reevaluating the source code will be to much effort.

QCheckbox issue [duplicate]

I am struggling to get this working.
I tried to transpose from a c++ post into python with no joy:
QMessageBox with a "Do not show this again" checkbox
my rough code goes like:
from PyQt5 import QtWidgets as qtw
...
mb = qtw.QMessageBox
cb = qtw.QCheckBox
# following 3 lines to get over runtime errors
# trying to pass the types it was asking for
# and surely messing up
mb.setCheckBox(mb(), cb())
cb.setText(cb(), "Don't show this message again")
cb.show(cb())
ret = mb.question(self,
'Close application',
'Do you really want to quit?',
mb.Yes | mb.No )
if ret == mb.No:
return
self.close()
the above executes with no errors but the checkbox ain't showing (the message box does).
consider that I am genetically stupid... and slow, very slow.
so please go easy on my learning curve
When trying to "port" code, it's important to know the basis of the source language and have a deeper knowledge of the target.
For instance, taking the first lines of your code and the referenced question:
QCheckBox *cb = new QCheckBox("Okay I understand");
The line above in C++ means that a new object (cb) of type QCheckBox is being created, and it's assigned the result of QCheckBox(...), which returns an instance of that class. To clarify how objects are declared, here's how a simple integer variable is created:
int mynumber = 10
This is because C++, like many languages, requires the object type for its declaration.
In Python, which is a dynamic typing language, this is not required (but it is possible since Python 3.6), but you still need to create the instance, and this is achieved by using the parentheses on the class (which results in calling it and causes both calling __new__ and then __init__). The first two lines of your code then should be:
mb = qtw.QMessageBox()
cb = qtw.QCheckBox()
Then, the problem is that you're calling the other methods with new instances of the above classes everytime.
An instance method (such as setCheckBox) is implicitly called with the instance as first argument, commonly known as self.
checkboxInstance = QCheckBox()
checkboxInstance.setText('My checkbox')
# is actually the result of:
QCheckBox.setText(checkboxInstance, 'My checkbox')
The last line means, more or less: call the setText function of the class QCheckBox, using the instance and the text as its arguments.
In fact, if QCheckBox was an actual python class, setText() would look like this:
class QCheckBox:
def setText(self, text):
self.text = text
When you did cb = qtw.QCheckBox you only created another reference to the class, and everytime you do cb() you create a new instance; the same happens for mb, since you created another reference to the message box class.
The following line:
mb.setCheckBox(mb(), cb())
is the same as:
QMessageBox.setCheckBox(QMessageBox(), QCheckBox())
Since you're creating new instances every time, the result is absolutely nothing: there's no reference to the new instances, and they will get immediately discarded ("garbage collected", aka, deleted) after that line is processed.
This is how the above should actually be done:
mb = qtw.QMessageBox()
cb = qtw.QCheckBox()
mb.setCheckBox(cb)
cb.setText("Don't show this message again")
Now, there's a fundamental flaw in your code: question() is a static method (actually, for Python, it's more of a class method). Static and class methods are functions that don't act on an instance, but only on/for a class. Static methods of QMessageBox like question or warning create a new instance of QMessageBox using the provided arguments, so everything you've done before on the instance you created is completely ignored.
These methods are convenience functions that allow simple creation of message boxes without the need to write too much code. Since those methods only allow customization based on their arguments (which don't include adding a check box), you obviously cannot use them, and you must code what they do "under the hood" explicitly.
Here is how the final code should look:
# create the dialog with a parent, which will make it *modal*
mb = qtw.QMessageBox(self)
mb.setWindowTitle('Close application')
mb.setText('Do you really want to quit?')
# you can set the text on a checkbox directly from its constructor
cb = qtw.QCheckBox("Don't show this message again")
mb.setCheckBox(cb)
mb.setStandardButtons(mb.Yes | mb.No)
ret = mb.exec_()
# call some function that stores the checkbox state
self.storeCloseWarning(cb.isChecked())
if ret == mb.No:
return
self.close()

Mypy: annotating a variable with a class type

I am having some trouble assigning the variables in a Python 3.6 class to a particular type--a Pathlib path. Following an example from link, I tried to create a TypeVar, but mypy is still throwing errors. I want to make sure that the class variables initialized in the __init__.py only receive a particular type at compile time. So this is just a check to make sure I don't inadvertently set a string or something else to these class variables.
Can anyone suggest the correct way to do this?
Here is some simple code.
import pathlib
from typing import Union, Dict, TypeVar, Type
Pathtype = TypeVar('Pathtype', bound=pathlib.Path)
class Request:
def __init__(self, argsdict):
self._dir_file1: Type[Pathtype] = argsdict['dir_file1']
self._dir_file2: Type[Pathtype] = argsdict['dir_file2']
The error that I am getting is:
Request.py:13: error: Invalid type "Request.Pathtype"
Request.py:14: error: Invalid type "Request.Pathtype"
Neither Type, TypeVar nor NewType are correct to use here. What you simply want to do is use Path itself:
from pathlib import Path
class Request:
def __init__(self, argsdict):
self._dir_file1: Path = argsdict['dir_file1']
self._dir_file2: Path = argsdict['dir_file2']
If you annotate your argsdict as being of type Dict[str, Path], you can skip having to annotate your fields entirely: mypy will infer the correct type:
from typing import Dict
from pathlib import Path
class Request:
def __init__(self, argsdict: Dict[str, Path]):
self._dir_file1 = argsdict['dir_file1']
self._dir_file2 = argsdict['dir_file2']
Here's a brief explanation of what the various type constructs you were attempting to use/was suggested to you actually do:
TypeVar is used when you are trying to create a generic data structure or function. For example, take List[int], which represents a list containing ints. List[...] is an example of a generic data structure: it can be parameterized by any arbitrary type.
You use TypeVar as a way of adding "parameterizable holes" if you decide you want to create your own generic data structure.
It's also possible to use TypeVars when writing generic functions. For example, suppose you want to declare that you have some function that can accept a value of any type -- but that function is guaranteed to return a value of the exact same type. You can express ideas like these using TypeVars.
The Type[...] annotation is used to indicate that some expression must be the type of a type. For example, to declare that some variable must hold an int, we would write my_var: int = 4. But what if we want to write something like my_var = int? What sort of type hint could we give that variable? In this case, we could do my_var: Type[int] = int.
NewType basically lets you "pretend" that you're taking some type and making a subclass of it -- but without requiring you to actually subclass anything at runtime. If you're careful, you can take advantage of this feature to help catch bugs where you mix different "kinds" of strings or ints or whatever -- e.g. passing in a string representing HTML into a function expecting a string representing SQL.
Replace TypeVar with NewType and remove the Type[] modifier.

Redirect logs to file in Pybel?

Similar to this post, but rather than change the logging level, I would like to redirect all logging information to a file. I believe the relevant API call is:
pybel.ob.obErrorLog.SetOutputStream()
or:
pybel.ob.OBMessageHandler().SetOutputStream()
But the SetOutputStream() method only accepts objects of type std::ostream *, not Python file streams, e.g. open('/path/to/log.txt', 'w') or sys.stdout.
Here are a few things I tried:
from openbabel import openbabel
import io
import sys
obHandler = openbabel.OBMessageHandler()
obHandler.SetOutputStream(io.BytesIO())
##Out: TypeError: in method 'OBMessageHandler_SetOutputStream', argument 2 of type 'std::ostream *'
obHandler.SetOutputStream(sys.stderr)
##Out: TypeError: in method 'OBMessageHandler_SetOutputStream', argument 2 of type 'std::ostream *'
with open("test.out", "w") as fd:
obHandler.SetOutputStream(fd)
##Out: TypeError: in method 'OBMessageHandler_SetOutputStream', argument 2 of type 'std::ostream *'
TL;DR: is there a reasonable way to direct log information to a file in PyBel?
One way work around this error is to capture the standard error stream.
Unfortunately one cannot directly redirect the standard error to an IOStream, as OpenBabel is operating on the underlying C standard error and unaware of the Python one.
But, thanks to this Stack Overflow answer, it is actually possible to capture the underlying error stream to a string.
With the OutputGrabber class loaded/imported, you can capture the output as so:
import sys
from openbabel import openbabel
obConversion = openbabel.OBConversion()
obConversion.SetInFormat("sdf")
mol = openbabel.OBMol()
out = OutputGrabber(sys.stderr)
with out:
success = obConversion.ReadString(mol, "INVALID\n\n")
And the text is now in out.capturedtext:
print(out.capturedtext)
==============================
*** Open Babel Warning in ReadMolecule
WARNING: Problems reading a MDL file
Cannot read comment line

Resources