Importing the .NET Framework Assemblies

0
166

Importing an assembly into IronPython isn’t much different from importing a Python module. In fact, you use about the same code. The primary difference is that you can’t import some .NET assemblies directly into IronPython, just as you can’t import them directly into any .NET language. Instead, you must first create a reference to the .NET assembly and then import it. For anyone who has worked with .NET languages in the past, nothing will have changed from the normal procedure they follow.

One odd thing about IronPython is that it’s case sensitive even when it comes to .NET Framework assemblies. As a consequence, importing system.math won’t work but importing System.Math will. Because many developers aren’t used to thinking about the case of .NET Framework assemblies, you might be caught off guard when an application fails for some unknown reason. One issue always to consider is whether you’ve capitalized the assembly name incorrectly.

Performing a Standard Import

As with Python modules, you can perform a standard import of a .NET assembly. For example, you might want to import the .NET Framework’s System assembly. In this case, you type

[code]

import System

[/code]

and press Enter. If you want to see what the System assembly contains, type

[code]

dir(System)

[/code]

and press Enter. Figure 7-1 shows typical results from importing the System assembly.

Now, let’s say that you want to create a UInt32 variable, just like a UInt32 that you’d create in any other .NET language. Simply type something like MyVar = System.UInt32(5). Of course, you can use any variable within the range that fits within a UInt32. If you don’t provide a value by typing MyVar = System.UInt32() the .NET Framework automatically assigns the variable a value of 0.

However, let’s take a look at MyVar. If you type MyVar by itself, you see that it’s an object that has a value of 5, as shown in Figure 7-2. Type dir(MyVar) and you see that MyVar contains many of the same methods as a standard Python integer. For example, you still have access to the absolute value function, __abs__(), and comparison method, __eq__(). In addition to these standard methods, you also have access to .NET-specific functions such as Parse() and ToChar().

Figure 7-1: Performing a standard import places the System assembly where you’d expect.
Figure 7-1: Performing a standard import places the System assembly where you’d expect.

When you import an assembly using the standard approach, some code can become long and cumbersome. For example, if you want to change the console foreground color, you must type the following:

[code]

System.Console.ForegroundColor = System.ConsoleColor.Blue

[/code]

Notice that you must use the correct enumeration when specifying the color, or the change won’t occur. This code really does work — give it a try and then print the current console foreground color, as shown in Figure 7-3 (the screenshot in this book shows only shades of gray, but you’ll see color on your display). The console color changes to whatever value you specify without creating a variable first because System.Console.ForegroundColor is a property.

Figure 7-2: Creating a .NET object provides a mix of Python and .NET methods.
Figure 7-2: Creating a .NET object provides a mix of Python and .NET methods.
Figure 7-3: Using the standard import can become a little cumbersome.
Figure 7-3: Using the standard import can become a little cumbersome.

As with Python modules, you can get around the problem by assigning a particular object to a variable. For example, if you type Console = System.Console, then you can shorten the code a little, as shown in Figure 7-3. The bottom line is that using a standard import with .NET isn’t much different from using it with Python modules. The only real difference is that you use a different name.

Importing an Assembly into the Global Namespace

Sometimes you need to have an entire assembly available at a global level. Using variables to bring part of the assembly up to the right level won’t work. In this case, you rely on a different import strategy than used in the section “Performing a Standard Import” earlier in this chapter. You’ve already seen this technique before as applied to Python modules, but now you’ll see how it applies to .NET assemblies. Simply use the from AssemblyName import Assembly | * format used for Python modules. For example, if you want to import the System assembly into the global namespace, you type

[code]

from System import *

[/code]

The asterisk (*) means that you import everything in the System assembly at the global namespace level. Figure 7-4 shows what happens when you use the dir() function to see the global namespace.

Figure 7-4: You can import an entire assembly into the global namespace.
Figure 7-4: You can import an entire assembly into the global namespace.

If you want a specific class within the System assembly imported at the global namespace level, you simply specify the name of the class as you would when working with Python. For example, if you want to work with the Console class, then you’d type

[code]

from System import Console

[/code]

Some developers will be tempted to import everything they need into the global namespace. While this strategy works fine for a .NET application created in a language such as C#, it doesn’t always work well in IronPython because of the way the Python language works. For example, Figure 7-4 shows an example of the problems that can occur. Imagine importing four or five assemblies into the global namespace and then using the dir() function to display a list of classes, methods, enumerations, or other .NET features you want to use. The list would be so large as to make any search pointless. Import only what you need into the global namespace.

You can extend individual imports by separating classes, enumerations, or other assembly members with commas. For example, if you want to import both the Console class and the ConsoleColor enumeration into the global namespace, you type

[code]

from System import Console, ConsoleColor

[/code]

If you use the dir() function to see the result, you see output similar to Figure 7-5. Importing only what you need keeps clutter down, makes your application run faster, and reduces potential security issues. In this case, you can reduce the foreground color-changing code shown in the “Performing a Standard Import” section to

[code]

Console.ForegroundColor = ConsoleColor.Blue

[/code]

Figure 7-5: Importing into the global namespace reduces the size and complexity of your code.
Figure 7-5: Importing into the global namespace reduces the size and complexity of your code.

Configuring the Console for .NET Help

Believe it or not, the help() function works fine with .NET assemblies. However, Microsoft designed the assembly help for a much larger display area. If you type help(ConsoleColor) and press Enter, the help output is so long that you can’t see even a small portion of it. In fact, you won’t actually see the help you need because it appears at the beginning of the help listing.

The console window has a buffer associated with it. When you type a command and the interpreter presents output, the buffer accepts all the output up to the size of the buffer. At that point, all the old information drops off the end into the bit bucket and you never see it again. The standard buffer size is 300 lines, which seems like it would be enough, but it isn’t nearly enough for the .NET help. What you really need for .NET help is about 3,000 lines. Use these steps to change the buffer size.

Figure 7-6: Modify the buffer to hold more lines of information.
Figure 7-6: Modify the buffer to hold more lines of information.
  1. Click the system menu in the upper-left corner of the console window and choose Properties from the context menu. You see the IronPython Console Properties dialog box.
  2. Select the Layout tab. You see the information shown in Figure 7-6.
  3. Change the Height property in the Screen Buffer Size area to 3000. This means that the screen buffer can now hold up the 3,000 lines of output. However, it also means that the screen buffer consumes ten times more memory, which means you won’t want to make this change to a console window unless you need the extra space.
  4. Click OK. You see the Apply Properties To Shortcut dialog box shown in Figure 7-7. If you plan to work with .NET very often, you’ll definitely want to choose “Modify Shortcut that Started this Window” so that you don’t have to make the change every time.
  5. Select one of the configuration change options and then click OK. Windows makes the change you requested.
Figure 7-7: Choose a configuration change option that matches your .NET usage habits.
Figure 7-7: Choose a configuration change option that matches your .NET usage habits.

At this point, you need to try out the help() function. Try typing

[code]

help(ConsoleColor)

[/code]

and press Enter. You’ll see that the display takes a second or so to return. At this point, you can scroll through the massive help display to find the information you need. Figure 7-8 shows typical output.

Creating a Reference to .NET Assemblies

Not every .NET assembly is available to IronPython by default, even if that assembly appears in the Global Assembly Cache (GAC). As with any other .NET language, you sometimes need to reference .NET assemblies in order to import and use them. For example, try typing

[code]

from System.Xml import *

[/code]

and press Enter. You get an error message as output stating the following:

[code]

from System.Xml import *
Traceback (most recent call last):
File “<stdin>”, line 1, in <module>
ImportError: No module named Xml

[/code]

Figure 7-8: The screen buffer is now large enough to hold the help information you need.
Figure 7-8: The screen buffer is now large enough to hold the help information you need.

Of course, you know that the System.Xml assembly does exist. This error message tells you that you have to add a reference to the System.Xml assembly before you can use it. In order to add a reference, you must import the clr (Common Language Runtime) module. You can then use one of the five following methods to import the assembly.

  • clr.AddReference(AssemblyObjectOrFilename): Adds a reference to the .NET assembly by passing an assembly object directly or by specifying the assembly filename. (You can provide either a partial or full filename.) This is a generic sort of assembly reference addition because you don’t have control over which assembly version IronPython loads. You can use this method when you’re experimenting and really don’t care about which version of the .NET assembly you get. This is also a good method to use when you’re not sure which version of the assembly the user has installed on his or her machine but do know that all versions of the .NET Framework include the functionality you require.
  • clr.AddReferenceToFile(AssemblyFilename[, AssemblyFilename…]): Adds a reference to the .NET assembly by passing a filename. You may supply multiple filenames to load multiple assemblies. IronPython looks for the assembly using the sys.path attribute. Consequently, you can partially control which version of the assembly you get by controlling the sys.path attribute. However, if more than one assembly has the correct filename, IronPython doesn’t guarantee which version of the assembly will load. You can use the clr.AddReferenceByName() method to better control which version of the assembly loads.
  • clr.AddReferenceToFileAndPath(AssemblyPathAndFilename[, AssemblyPathAndFilename …]): Performs about the same task as the clr .AddReferenceToFile() method. However, in this case, you must provide an absolute path to the assembly you want to load, which means that you have better control over which assembly version loads. This method automatically adds the assembly path to sys.path for you.
  • clr.AddReferenceByName(AssemblyName, Version=VersionNumber, Culture= CultureIdentifier|neutral, PublicKeyToken=TokenValue): Adds an assembly reference based on assembly specifics normally found in the GAC. You must supply values that fully define the assembly. For example, to import the .NET Framework 2.0 version of the System. Xml assembly, you would supply: ‘System.Xml, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089‘.
  • clr.AddReferenceByPartialName(PartialAssemblyName): Adds a reference to the .NET assembly by passing a partial name that IronPython looks up in the GAC. This method doesn’t assure that you obtain any particular version of the assembly you need. You can use the clr.AddReferenceByName() method to better control which version of the assembly loads.

Now that you have a better idea of how to add a reference, let’s try importing the System.Xml assembly. The following steps help you get the assembly referenced and imported into IronPython.

  1. Type import sys and press Enter. This step makes the sys module accessible.
  2. Type sys.path.append(‘C:\WINDOWS\Microsoft.NET\Framework\v2.0.50727‘) and press Enter. In order to add a reference to an assembly, you must provide its location as part of sys.path. You may need to change the drive and directory to match your system.
  3. Type import clr and press Enter. This step makes the clr module accessible, which has the various assembly reference methods described earlier.
  4. Type clr.AddReference(‘System.Xml.DLL‘) and press Enter. IronPython now has a reference to the assembly file it needs, but the assembly isn’t imported yet. If you receive an IO Error message, it means that IronPython couldn’t find the assembly you requested in the location provided as part of sys.path.
  5. Type import System.Xml and press Enter. The System.Xml assembly is now available for use. It’s time to test to verify that the assembly is available.
  6. Type dir(System.Xml) and press Enter. You should see the content of the System.Xml assembly, as shown in Figure 7-9.

This technique works with any .NET assembly, not just those found in the GAC. If you have a custom .NET assembly you want to use in your application, this technique lets you access it with ease. Make sure you use the right technique for the kind of assembly you want to import. If version number is important, then make sure you use the clr.AddReferenceByName() method.

Figure 7-9: Verify that you can access the System.Xml assembly.
Figure 7-9: Verify that you can access the System.Xml assembly.

If you import a module or assembly by mistake, you can unload it in the same way as you remove variables you no longer need, by typing del <NameOfModuleOrAssembly>. For example, if you want to get rid of the System.Xml assembly after using it, type del System and press Enter. The module or assembly you want to remove must appear in the dir() list. In this case, when you type dir() after importing System.Xml, you see System, not System.Xml in the dir() list, so you must del System, not del System.Xml. Never set an assembly or module reference to None (as you would for clearing a variable) because the reference will remain, but none of the content will exist, causing hard to find errors in your application.