Namespaces with Module Imports

Namespaces with Module Imports

As the traceback shows, the problem isn't in main.py, but in module1.py:

Traceback (most recent call last):
File "Z:\Python\main.py", line 10, in <module>
module1.cool()
File "Z:\Python\module1.py", line 3, in cool
print pi
NameError: global name 'pi' is not defined

In other words, in module1, there is no global name pi, because you haven't imported it there. When you do from math import * in main.py, that just imports everything from the math module's namespace into the main module's namespace, not into every module's namespace.

I think the key thing you're missing here is that each module has its own "global" namespace. This can be a bit confusing at first, because in languages like C, there's a single global namespace shared by all extern variables and functions. But once you get past that assumption, the Python way makes perfect sense.

So, if you want to use pi from module1, you have to do the from math import * in module1.py. (Or you could find some other way to inject it—for example, module1.py could do from main import *, or main.py could do module1.pi = pi, etc. Or you could cram pi into the magic builtins/__builtin__ module, or use various other tricks. But the obvious solution is to do the import where you want it imported.)


As a side note, you usually don't want to do from foo import * anywhere except the interactive interpreter or, occasionally, the top-level script. There are exceptions (e.g., a few modules are explicitly designed to be used that way), but the rule of thumb is to either import foo or use a limited from foo import bar, baz.

How to use namespaces with import in TypeScript

A solution with namespaces (not recommended)

To resolve your issue, you can export your namespace:

// UtilBase.ts
import * as path from "path";
export namespace My.utils {
export class UtilBase {
protected fixPath(value: string): string {
return value.replace('/', path.sep);
}
}
}

Then, you should be able to import it:

// UtilOne.ts
import {My} from './UtilBase';
namespace My.utils {
export class UtilOne extends My.utils.UtilBase {
}
}

However, if the purpose is to organize the code, it is a bad practice to use namespaces and (ES6) modules at the same time. With Node.js, your files are modules, then you should avoid namespaces.

Use ES6 modules without namespaces

TypeScript supports the syntax of ES6 modules very well:

// UtilBase.ts
import * as path from "path";
export default class UtilBase {
protected fixPath(value: string): string {
return value.replace('/', path.sep);
}
}

// UtilOne.ts
import UtilBase from './UtilBase';
export default class UtilOne extends UtilBase {
}

It is the recommended way. ES6 modules prevent naming conflicts with the ability to rename each imported resource.

It will work on Node.js.

For a good introduction to the ES6 modules syntax, read this article.

Use a file tsconfig.json instead of /// <reference

Notice: The syntax /// <reference is replaced by the file tsconfig.json. An example for Node.js:

// tsconfig.json
{
"compilerOptions": {
"module": "commonjs",
"target": "es6"
},
"exclude": [
"node_modules"
]
}

Python scopes and namespaces of imported modules

Python module importing and global namespace management is a very broad topic, so I will limit this answer to the specific case that you are seeing.

In general, Python works very sequentially.

  1. You open the interpreter, certain special values such as environment variable and other kernel variables are injected into the global namespace.
  2. You execute import test2, this goes to the test2.py file in the local directory and executes that file in the same Python environment.
  3. While executing test2.py, Python comes across from test1 import test1.
  4. Similar to step 2, Python goes and finds test1.py, it does some extra things because you are importing a function in the module rather than the module itself, but in general, at this point, test1 (the function) gets placed in the current scope.
  5. You call test2.test2(), since test2 has been imported in step 2, it exists and so does test2.test2. So the interpreter jumps to test2.test2 and test1 is called and since it is in namespace (we just imported it in step 3) no error is thrown, then your second print statement is called.

Things are obviously a bit more complicated than this on the low-level, but this does give a general overview of how Python deals with this stuff.

How to import library modules to my namespace from another module?

Ok, after trying for a while I realised that I should pass globals() as a parameter to the function and pass it over to the exec in order to make it work in my namespace.

The function now looks like this:

import importlib
def megaimport(globals_dict, library_name):
skip_modules = ["sys", "os", "dir_path"]
mod = importlib.import_module(library)
for module in dir(mod):
if module.startswith("__") == False and module not in skip_modules:
exec(f"from {library_name}.{module} import *", globals_dict)

And it can be called just by:

from utils import megaimport
megaimport(globals(),"library")

Module vs Namespace - Import vs Require Typescript

I didn't get how we categorize them?

Namespaces are used to organize/encapsulate your code. External modules are used to organize/encapsulate your code AND to locate your code at runtime. In practice, you have two choices at runtime: 1) combine all transpiled code into one file, or 2) use external modules and have multiple files and require some other mechanism to get at those files.

When to export a class or namespace or package?

To make a type or value visible outside of the file that it's in, you have to export it if it's inside of a namespace. Whether you export it at the top level or within a namespace will decide if it's now in an external module.

If we export package/namespace, all classes within that are exported or need to be explicitly exported

Classes in a namespace will always need to be explicitly exported for the class to be visible at compile time outside of the file in which it is defined.

How each one of them can be imported/required?

This depends of if you're using external modules. An external module will always need to be imported to "use" it. Importing a namespace that's not in an external module is really just providing an alias for the namespace -- you still have to prefix the type/whatever with the alias (and this is why you generally don't want to use namespaces with external modules; doing so means you always have to use a prefix when referencing anything provided by the external module.) Namespaces that aren't in an external module can span files, so if you're in the same namespace you can refer to anything exported by the namespace without needing any sort of import.

To really understand the above you need some background knowledge. The key thing to understand with references/namespaces/external modules is what these constructs do at compile time and what they do at runtime.

Reference directives are used at compile time to locate type information. Your source has a particular symbol in it. How does the TypeScript compiler locate the definition for that symbol? The reference directive has largely been subsumed by the tsconfig.json mechanism -- using tsconfig.json, you tell the compiler where all your sources are.

Namespaces can contain type definitions and/or implementation. If a namespace contain only type information then it has no runtime manifestation at all -- you can check this by looking at the JS output and finding an empty JS file. If a namespace has implementation code, then the code is wrapped inside a closure that is assigned to a global variable with the same name as the namespace. With nested namespaces, there will be a global variable for the root name space. Again, check the JS output. Namespaces are historically how JS client-side libraries have attempted to avoid the issue with naming collisions. The idea is to wrap your entire library into one closure and then expose as small a global footprint as possible -- just one global variable referencing the closure. Well, the problem is still that you've claimed a name in the global space. What if you wanted, say, two versions of a library? A TypeScript namespace still has the issue of how to locate the source for the namespace. That is, source code that references A.B still has the problem of telling the compiler how to locate A.B -- either by using reference directives or by using tsconfig.json. Or by putting the namespace into an external module and then importing the external module.

External modules originated with server-side JS. There is a one-to-one correspondence between an external module and a file on the file system. You can use the file system directory structure to organize external modules into a nested structure. Importing an external module will generally aways introduce a runtime dependency on that external module (the exception is when you import an external module but then don't use any of its exports in the value position -- that is, you only import the external module to get at its type information). An external module is implicitly in a closure, and this is key: the user of the module can assign the closure to whatever local variable they want. TypeScript/ES6 adds additional syntax around mapping the exports of the external modules to local names, but this is just a nicety. On the server side, locating an external module is relatively straight forward: just locate the file representing the external module on the local file system. If you want to use external modules on the client side, in a browser, it gets more complex as there's no equivalent to the file system that has the module available for loading. So now on the client side you need a way to bundle all those files into a form that can be used remotely in the browser -- this is where module bundlers like Webpack (Webpack does a heck of a lot more than bundle modules though) and Browserify come into play. Module bundlers allow runtime resolution of your external modules in the browser.

Real world scenario: AngularJS. Pretend external modules don't exist, use a single namespace to limit pollution of global space (in the example below a single variable MyApp is all that is in the global space), export only interfaces, and use AngularJS dependency-injection to make implementations available for use. Put all classes in a directory root, add a tsconfig.json to the root, install angularjs typings under the same directory root so that tsconfig.json picks it up too, combine all output int one JS file. This will work fine for most projects if code-reuse isn't much of a concern.

MyService.ts:

namespace MyApp {

// without an export the interface is not visible outside of MyService.ts
export interface MyService {
....
}

// class is not exported; AngularJS DI will wire up the implementation
class MyServiceImpl implements MyService {
}

angular.module("MyApp").service("myService", MyServiceImpl);
}

MyController.ts:

namespace MyApp {

class MyController {
// No import of MyService is needed as we are spanning
// one namespace with multiple files.
// MyService is only used at compile time for type checking.
// AngularJS DI is done on the name of the variable.
constructor(private myService: MyService) {
}
}
angular.module("MyApp").controller("myController", MyController);
}

Using IIFE to avoid polluting global runtime scope. In this example, no global variables are created at all. (A tsconfig.json is assumed.)

Foo.ts:

namespace Foo {
// without an export IFoo is not visible. No JS is generated here
// as we are only defining a type.
export interface IFoo {
x: string;
}
}

interface ITopLevel {
z: string;
}

(function(){
// export required above to make IFoo visible as we are not in the Foo namespace
class Foo1 implements Foo.IFoo {
x: string = "abc";
}
// do something with Foo1 like register it with a DI system
})();

Bar.ts:

// alias import; no external module created
import IFoo = Foo.IFoo;

(function() {

// Namespace Foo is always visible as it was defined at
// top level (outside of any other namespace).
class Bar1 implements Foo.IFoo {
x: string;
}

// equivalent to above
class Bar2 implements IFoo {
x: string;
}

// IToplevel is visible here for the same reason namespace Foo is visible
class MyToplevel implements ITopLevel {
z: string;
}

})();

Using IIFE you can make eliminate introducing MyApp as a global variable in the first example.

MyService.ts:

interface MyService { 
....
}

(function() {

class MyServiceImpl implements MyService {
}

angular.module("MyApp").service("myService", MyServiceImpl);
})();

MyController.ts:

(function() { 

class MyController {
constructor(private myService: MyService) {
}
}

angular.module("MyApp").controller("myController", MyController);
})();

Difference between import 'namespace/module' and import '@namespace/module' with TypeScript and Yarn?

The two different import paths mean two different things:

  1. import "@firebase/auth" means that you are importing the @firebase/auth NPM package. This means that the package itself is published in NPM as @firebase/auth
  2. import "firebase/auth" means that you are importing the auth folder from the firebase package

Looking at the @firebase/auth readme it says

This package is not intended for direct usage, and should only be used via the officially supported firebase package.

That seems to support the docs that you should be using the firebase package and importing one of its subfolders.

Re your other question: which package did you actually npm install?

Because if you are using the firebase package that is what you should have installed, not firebase/auth - which is not its own NPM module - or @firebase/auth which is not meant for direct use.

If you did install @firebase/auth that would explain why TypeScript can't find the types in the firebase/auth path as that's not the same package you installed.

How is the use of import modulename related to namespace of imported module?

Lets say you are doing

import os 

you can now access members of os, for example

os.path

but you cant access path directly as it's not in your namespace, it's in the os namespace.

It's the difference between

import os 

and

from os import * 

(the latter will import everything into your namespace and should generally be avoided to not accidentally overwrite methods)



Related Topics



Leave a reply



Submit