Commit 4e10a4f0 authored by Kevin Modzelewski's avatar Kevin Modzelewski Committed by GitHub

Merge pull request #1378 from kmod/typing

Add test for typing.py and fix the issues it uncovers
parents 3f1a0e18 bfb0ec4a
......@@ -41,9 +41,14 @@ int64_t ICInvalidator::version() {
}
ICInvalidator::~ICInvalidator() {
for (ICSlotInfo* slot : dependents) {
slot->invalidators.erase(std::find(slot->invalidators.begin(), slot->invalidators.end(), this));
}
// It's not clear what we should do if there are any dependencies still tracked
// when this object is deleted. The most likely thing is that we should invalidate
// them, since whatever caused the destruction is most likely an invalidation event
// as well.
// For now, let's just assert on this to know if it happens. In the unlikely
// case that we want to just ignore any existing dependents (old behavior), we
// can add a new API call to forget them (and remove from the dependents' `invalidators` list)
ASSERT(dependents.empty(), "dependents left when ICInvalidator destructed");
}
void ICInvalidator::addDependent(ICSlotInfo* entry_info) {
......
......@@ -165,6 +165,7 @@ public:
void appendAttrwrapper();
void delAttribute(BoxedString* attr);
void addDependence(Rewriter* rewriter);
void invalidateAll() { dependent_getattrs.invalidateAll(); }
friend class HiddenClass;
};
......
......@@ -636,6 +636,10 @@ void BoxedClass::freeze() {
assert(!is_constant);
assert(tp_name); // otherwise debugging will be very hard
auto doc_str = getStaticString("__doc__");
if (!hasattr(doc_str))
giveAttr(incref(doc_str), boxString(tp_name));
fixup_slot_dispatchers(this);
if (instancesHaveDictAttrs() || instancesHaveHCAttrs()) {
......@@ -1318,8 +1322,10 @@ void HCAttrs::_clearRaw() noexcept {
auto old_attr_list_size = hcls->attributeArraySize();
// singleton classes will not get reused so free it
if (hcls->type == HiddenClass::SINGLETON)
if (hcls->type == HiddenClass::SINGLETON) {
hcls->getAsSingleton()->invalidateAll();
delete hcls;
}
new ((void*)this) HCAttrs(NULL);
if (old_attr_list) {
......
......@@ -158,7 +158,7 @@ BoxedSet* makeNewSet(BoxedClass* cls, Box* container) {
}
Box* frozensetNew(Box* _cls, Box* container, BoxedDict* kwargs) {
RELEASE_ASSERT(_cls->cls == type_cls, "");
RELEASE_ASSERT(PyType_Check(_cls), "");
BoxedClass* cls = static_cast<BoxedClass*>(_cls);
RELEASE_ASSERT(isSubclass(cls, frozenset_cls), "");
if (_cls == frozenset_cls && !_PyArg_NoKeywords("frozenset()", kwargs)) {
......@@ -191,7 +191,7 @@ Box* frozensetNew(Box* _cls, Box* container, BoxedDict* kwargs) {
}
Box* setNew(Box* _cls, Box* container, BoxedDict* kwargs) {
RELEASE_ASSERT(_cls->cls == type_cls, "");
RELEASE_ASSERT(PyType_Check(_cls), "");
BoxedClass* cls = static_cast<BoxedClass*>(_cls);
RELEASE_ASSERT(isSubclass(cls, set_cls), "");
......
......@@ -1604,7 +1604,11 @@ static Box* function_new(BoxedClass* cls, Box* _code, Box* globals, Box** _args)
Box* defaults = _args[1];
Box* closure = _args[2];
RELEASE_ASSERT(PyCode_Check(_code), "");
if (!PyCode_Check(_code)) {
PyErr_Format(TypeError, "function() argument 1 must be code, not %200s", _code->cls->tp_name);
return NULL;
}
BoxedCode* code = static_cast<BoxedCode*>(_code);
if (name != Py_None && !PyString_Check(name)) {
......@@ -1787,6 +1791,17 @@ static Box* functionNonzero(BoxedFunction* self) {
Py_RETURN_TRUE;
}
static Box* im_doc(Box* b, void*) noexcept {
RELEASE_ASSERT(b->cls == instancemethod_cls, "");
auto doc_str = getStaticString("__doc__");
try {
return getattr(static_cast<BoxedInstanceMethod*>(b)->func, doc_str);
} catch (ExcInfo e) {
setCAPIException(e);
return NULL;
}
}
extern "C" {
Box* Py_None = NULL;
Box* NotImplemented = NULL;
......@@ -4231,6 +4246,10 @@ void setupRuntime() {
Py_INCREF(Py_None);
object_cls->giveAttr("__base__", Py_None);
object_cls->giveAttr("__doc__", boxString("The most base type"));
type_cls->giveAttr("__doc__",
boxString("type(object) -> the object's type\ntype(name, bases, dict) -> a new type"));
// Not sure why CPython defines sizeof(PyTupleObject) to include one element,
// but we copy that, which means we have to subtract that extra pointer to get the tp_basicsize:
tuple_cls = new (0) BoxedClass(object_cls, 0, 0, sizeof(BoxedTuple) - sizeof(Box*), false, "tuple", true,
......@@ -4569,10 +4588,14 @@ void setupRuntime() {
instancemethod_cls->giveAttrBorrowed("__func__", instancemethod_cls->getattr(getStaticString("im_func")));
instancemethod_cls->giveAttrMember("im_self", T_OBJECT, offsetof(BoxedInstanceMethod, obj));
instancemethod_cls->giveAttrBorrowed("__self__", instancemethod_cls->getattr(getStaticString("im_self")));
instancemethod_cls->freeze();
instancemethod_cls->giveAttrMember("im_class", T_OBJECT, offsetof(BoxedInstanceMethod, im_class), true);
// TODO: this should be handled via a getattro instead (which proxies to the function):
instancemethod_cls->giveAttrDescriptor("__doc__", im_doc, NULL);
instancemethod_cls->freeze();
slice_cls->giveAttr("__new__",
new BoxedFunction(BoxedCode::create((void*)sliceNew, UNKNOWN, 4, false, false, "slice.__new__"),
{ NULL, Py_None }));
......
import os, sys, subprocess, shutil
sys.path.append(os.path.dirname(__file__) + "/../lib")
from test_helper import create_virtenv, run_test
ENV_NAME = "typing_test_env_" + os.path.basename(sys.executable)
ENV_DIR = os.path.abspath(ENV_NAME)
PYTHON_EXE = os.path.abspath(os.path.join(ENV_NAME, "bin", "python"))
pkg = ["typing==3.5.2.2"]
# pkg = ["git+https://github.com/python/typing.git@3.5.2.2"]
create_virtenv(ENV_NAME, pkg, force_create=False)
# subprocess.check_call([PYTHON_EXE, os.path.join(ENV_DIR, "lib", "python2.7", "site-packages", "typing", "test_typing.py")])
# subprocess.check_call([PYTHON_EXE, "-m", "test_typing"])
print "Running some extra tests:"
test_fn = os.path.join(ENV_DIR, "test.py")
with open(test_fn, 'w') as f:
f.write( """
import sys
from typing import Generic, TypeVar, Sequence
RR = TypeVar('RR')
for i in xrange(1000):
Generic[RR]
Sequence[RR]
print "Passed"
""")
subprocess.check_call([PYTHON_EXE, test_fn])
print "Running typing's own tests:"
import urllib
test_file = urllib.urlopen("https://raw.githubusercontent.com/python/typing/1e4c0bae6f797ee5878ce4bb30f3b03c679e3e11/python2/test_typing.py").read()
test_fn = os.path.join(ENV_DIR, "test_typing.py")
with open(test_fn, 'w') as f:
f.write(test_file)
subprocess.check_call([PYTHON_EXE, test_fn])
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment