Skip to content

Tags: ynkdir/py-winmd

Tags

v2.4.0

Toggle v2.4.0's commit message
winmd for Python 2.4.0

The three searches are methods of a table, where they were methods of the
database, and that is the one thing code written against 2.3.0 will notice:

    db.equal_range(InterfaceImpl, 0, n)  ->  db.InterfaceImpl.equal_range(0, n)
    db.find_row(PropertyMap, 0, n)       ->  db.PropertyMap.find_row(0, n)
    db.parent_row(TypeDef, 4, n)         ->  db.TypeDef.parent_row(4, n)

They read one table's rows and one table's column cache, and build rows of
that table's class. The cache moved onto the table in 2.3.0 and these did
not follow, so each looked its table up and then looked it up again. The
C++ hands the table to equal_range as well - `equal_range(get_database().
GenericParam, coded_index<TypeOrMethodDef>())` - so the table is the
subject there too; what cannot be a free function here is the cache.

Naming the table also gets the rows checked. table_of(TableNumber.X)
returns a Table of no particular row, so nothing could be said about what
came back; db.InterfaceImpl says it.

database.py is 63 lines lighter, and none of equal_range, find_row,
parent_row, the column cache or its builder is left on it.

Assembly.Version and AssemblyRef.Version read the column as one number and
shift the four out of it, as Assembly::Version does, rather than working
out where the bytes are. Only those two have a version, and the arithmetic
they were doing reached into three private fields of the table and one of
the database.

v2.3.0

Toggle v2.3.0's commit message
winmd for Python 2.3.0

table.py is laid out as impl/winmd_reader/table.h is, and three things
that code written against 2.2.0 may notice come out of that.

A table is an object now, not four dicts on the database. table_base holds
one table's row count, row size, column widths and bytes - what table_base
does in the C++ - and Table is table<Row> over it. A row, a range, a list
and a coded index each hold one, and reach the file through it, which is
what row_base and index_base hold. Reading a row was four dict lookups
keyed by TableNumber before any bytes were read; it is now four attribute
reads, and rows that come out of a table or a range cost nothing to place
at all.

    cache it                    0.91x
    walk every signature in it  0.96x
    name every attribute        0.93x

Row.get_coded_index is _get_coded_index. row_base draws a protected: line
and get_string, get_blob and get_coded_index sit under it - they are for
the thirty-eight structs built on the base, not for a caller. The first
two were already private here; the third I had made public in 2.2.0 and
should not have.

A signature is built from a table and a blob - MethodDefSig(table, blob) -
where 2.2.0 took a database and 2.1.0 took the blob alone. signature.h
carries a table_base const* beside the view and hands both to every
signature it builds, and byte_view has no business holding either: it is
bytes and a cursor, and byte_view.table is gone with the field.

Added: table_base, database.table_of(number), and four on every row that
row_base has and this did not - get_list, get_target_row, get_parent_row
and coded_index, the last of which hands back the value a column would
hold rather than an index built around it. table_base.index_size and
Table.row / Table.rows / Table.number come with the table object.

Moved: EnumDefinition, which reads a TypeDef's fields and is not a
signature, from signature.py to helpers.py, where key.h keeps it.

v2.2.0

Toggle v2.2.0's commit message
winmd for Python 2.2.0

winmd.reader is a package now, ten modules named after the C++ headers
they answer to, and the interface changes in four ways that code written
against 2.1.0 may notice.

get_row() takes the row class, as the C++ get_row<TypeDef>() does, where
it took a TableNumber:

    index.get_row(TypeDef)          not  index.get_row(TableNumber.TypeDef)

coded_index is no longer generic. A kind is a value here rather than a
type parameter, so there is no coded_index[TypeDefOrRef] to write in an
annotation; each kind is a class, named coded_index_TypeDefOrRef, and
that is what an annotation says. index.type() is still that kind's enum.

byte_view.coded_index() is gone. The C++ view has no such method - a
signature builds coded_index<TypeDefOrRef> where it needs one, and so
does signature.py now.

TypeSig.value_type is for the checker alone and is not a runtime object,
so isinstance(x, TypeSig.value_type) no longer answers. A variant is not
a type to test against in C++ either. Narrow with the concrete class:
isinstance(value, coded_index_TypeDefOrRef).

Names the old single module leaked - IntEnum, struct, mmap, Sequence,
dataclass and fourteen others it had imported - are no longer attributes
of winmd.reader. __all__ says what it offers, which is the 137 names its
ten modules define.

Added: coded_index.of(kind, database, value), Row.get_coded_index(kind,
column), CodedIndexKind for the thirteen kinds, and carries_attributes,
the Protocol get_attribute takes.

Fixed: CustomAttribute.TypeNamespaceAndName read a MemberRefParent with
TypeDefOrRef's enumerators, where tag 2 is TypeSpec in one and ModuleRef
in the other, so the guard never fired and a ModuleRef or MethodDef
parent reached TypeNamespace() on a row that has none. It raises now, as
custom_attribute.h does.

examples/windows.py runs as a script again, and finds the Win32 metadata
under vendor/ without being told where it is.

v2.1.0

Toggle v2.1.0's commit message
winmd for Python 2.1.0

The interface loses six accessors and changes two, so code written
against 2.0.0 may not run against this.

Gone, all of them a second name for an accessor beside them, none of them
in the C++ or in ECMA-335, and none called anywhere in this tree:

    Event.Flags             say EventFlags
    Event.Type              say EventType
    MethodSemantics.Flags   say Semantic
    ImplMap.Flags           say MappingFlags
    ImplMap.Name            say ImportName
    Property.Signature      say Type, which is the column's name

Changed: PInvokeAttributes.CharSet and .CallConv returned bool for fields
two and three bits wide, so CallConv was true for every row and said
nothing about which convention. They return the new CharSet and CallConv
enums, whose members ECMA-335 II.23.1.8 names.

Beside the package, the examples grew a good deal. windows.py resolves
Win32 and WinRT alike from one front door; it, ctypes_gen.py and
dumpwin32.py now take the definition meant for the architecture in hand,
where a few hundred Win32 names have one per CPU, and give a fixed size
array the length its signature carries rather than making it a pointer.
docs/winmd-format.md says what is in a .winmd file in enough detail to
write a reader from.

v2.0.0

Toggle v2.0.0's commit message
winmd for Python 2.0.0

The interface moved to the C++ reader's names, which is what makes this a
major version: code written against 1.0.x will not compile against it.

  - a coded index is a class per kind, coded_index_TypeDefOrRef and the
    twelve others, with an accessor per table its tag can name
  - a row is a class per table, carrying only the accessors that table has
  - the enums and the flags columns are named and valued as enum.h and
    flags.h have them, including the enumerators that were missing
  - the tables are a TableNumber enum rather than module constants, and
    __all__ is what the module offers
  - database and byte_view are the C++ spellings, everywhere

Behaviour is held to the C++ reader over the real metadata by the
reference suite, as before.

Around it: the whole package is annotated and pyrefly checks it, ruff owns
the layout, both run in CI beside the suites, and the examples grew a
merged windows.py that resolves Win32 and WinRT alike from the metadata.
docs/winmd-format.md says what is in a .winmd file.

v1.0.2

Toggle v1.0.2's commit message
winmd for Python 1.0.2

v1.0.1

Toggle v1.0.1's commit message
winmd for Python 1.0.1

v1.0.0

Toggle v1.0.0's commit message
winmd for Python 1.0.0