Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 8 additions & 3 deletions src/asm_writing/icinfo.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down
1 change: 1 addition & 0 deletions src/runtime/hiddenclass.h
Original file line number Diff line number Diff line change
Expand Up @@ -165,6 +165,7 @@ class HiddenClassSingleton final : public HiddenClassSingletonOrNormal {
void appendAttrwrapper();
void delAttribute(BoxedString* attr);
void addDependence(Rewriter* rewriter);
void invalidateAll() { dependent_getattrs.invalidateAll(); }

friend class HiddenClass;
};
Expand Down
8 changes: 7 additions & 1 deletion src/runtime/objmodel.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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()) {
Expand Down Expand Up @@ -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) {
Expand Down
4 changes: 2 additions & 2 deletions src/runtime/set.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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)) {
Expand Down Expand Up @@ -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), "");

Expand Down
27 changes: 25 additions & 2 deletions src/runtime/types.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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)) {
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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 }));
Expand Down
42 changes: 42 additions & 0 deletions test/integration/typing_test.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
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/[email protected]"]

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])